# Wix Developer Portals ## [Develop Websites with Velo](https://dev.wix.com/docs/develop-websites.md) > Extend Wix sites using Velo's JavaScript-based coding environment with access to page elements, backend modules, and Wix APIs > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Developing Websites ## Article: About Velo by Wix ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md ## Article Content: # About Developing Websites Developing websites on Wix using [Velo APIs](https://dev.wix.com/docs/velo.md) offers a unique blend of flexibility, ease of use, and powerful features. Whether you're a seasoned developer or just starting, Wix provides the tools and technologies needed to craft visually appealing and fully functional dynamic web applications: - **Interactive UI:** Use Wix's powerful editors to build sites with custom UI, behaviors, and interactions using the [$w() API](https://www.wix.com/velo/reference/$w). - **Full-stack development:** Leverage a robust suite of developer tools and Wix APIs to build and deploy with efficiency and security. Integrate your code with the editor UI to enhance and extend its capabilities. - **Content management:** Use the Wix CMS and its data APIs to manage, serve, and display content dynamically at scale. - **Wix business solutions:** Connect directly to production-ready solutions like eCommerce and Bookings, and extend them to deliver a tailored experience for site visitors. - **Zero-setup backend:** Implement complex workflows and automations with a fully supported Node.js environment. Wix handles infrastructure, hosting, and scaling so you can focus on your code. - **3rd-party integrations:** Expand a site's capabilities by adding apps, npm packages, and external services such as payment providers and marketing tools. > **Note:** Until now, custom code on Wix sites was implemented using Velo APIs. You can now use the JavaScript SDK for most functionality. Learn more about [Developing Websites with the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md) and how to [migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). ## Technologies Build with trusted, industry-standard tools tailored to Wix's platform. Utilize your existing skills and leverage the following technologies to create and manage dynamic web applications: - **JavaScript**: When developing a site with Wix, you write both frontend and backend code in JavaScript, calling Wix's JavaScript APIs to incorporate interactive and dynamic functionality. - **Node.js**: Wix provides a robust Node.js environment for running your backend code. - **MongoDB**: Wix uses MongoDB as the database for its CMS system, offering a flexible and scalable solution for managing and storing content. ## Development environments Pick the environment that fits how you build. Wix offers several [development environments](https://dev.wix.com/docs/develop-websites/articles/get-started/development-environments.md) to match your workflow: - **Editors:** Use the built-in [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) in Wix Studio or the Wix Editor to write and manage code alongside the visual site editor. No setup required. - **Local IDE:** Use a [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md), such as VS Code, for the flexibility and power of a full-featured development environment on your own machine. Set up via GitHub integration and manage your project using the CLI. Pair with [AI tools](#wix-ai-tools) to provide local AI coding clients the context they need to build and manage your project. - **Wix IDE (Beta):** Use the [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md), a VS Code-based online IDE for Wix Studio sites. ## Wix AI tools For AI coding clients like Claude Code, Cursor, and VS Code, install the [Wix Plugin](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-the-wix-plugin.md) to give your AI client the context it needs to build and manage your project. The Wix Plugin includes: - [Wix Skills](https://github.com/wix/skills/blob/main/skills): A collection of self-contained instruction sets for Wix development tasks. This includes the Wix Headless skill, which can scaffold and deploy a complete headless project from a single prompt. - [Wix MCP](https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/about-the-wix-mcp.md): A Model Context Protocol server that lets your AI tool search Wix docs, scaffold code, and call Wix APIs directly from your IDE. To install just Wix Skills, run: ```bash npx skills add wix/skills ``` > **Note:** Add `-g` to install globally and make Wix Skills available across all your projects. ### Programmatic docs access Beyond the Wix Plugin, you can pull Wix docs into any tool or workflow: - **llms.txt index:** Browse a structured index of all documentation at [dev.wix.com/docs/llms.txt](https://dev.wix.com/docs/llms.txt.md). - **Markdown format:** Append `.md` to any documentation page URL to retrieve the Markdown version of that page. - **Page menu:** Use the **Ask Assistant** dropdown on any documentation page to copy the page as Markdown or get a direct link to the Markdown version. ## Developer journey Ready to get started? Here's what to expect at each stage of the development process, from a blank canvas to a fully functioning site: - **Get started:** Explore the [Wix ecosystem](https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md) and browse the [API Reference](https://dev.wix.com/docs/api-reference.md) and potential [3rd-party integrations](https://dev.wix.com/docs/develop-websites/articles/get-started/integrate-with-3rd-parties.md) to see what you can build. Take some time to review best practices and any limitations to ensure a smooth development process. > **Note:** Until now, custom code on Wix sites was implemented using Velo APIs. You can now use the JavaScript SDK for most functionality. Learn more about [Developing Websites with the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md) and how to [migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). - **Code:** [Start coding](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md) using 1 or more Wix [development environments](https://dev.wix.com/docs/develop-websites/articles/get-started/development-environments.md) to write, manage, and debug your code. Build both frontend and backend to deliver a fully functional and user-friendly site. - **Test:** Preview and thoroughly test your site throughout development. Use the built-in tools to test your code in isolation and as a whole. Create [test sites](https://support.wix.com/en/article/about-test-sites) to experiment without affecting a live site, and use [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) to monitor and debug issues. - **Publish:** Use the editor or the [CLI](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/publishing-a-site-with-git-integration-wix-cli-for-sites.md) to publish your site once you've thoroughly tested it. - **Maintain:** Monitor your site using [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) and the [monitoring dashboard](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/monitoring-your-published-site/working-with-the-monitoring-dashboard.md) to track performance and resource usage. Consider [upgrading your premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md) to scale your site effectively and avoid hitting plan limitations. - **Grow:** Become a [Certified Wix Partner](https://www.wix.com/partners/partner-program) to offer your development services or sell apps to an audience of 250M+ users. ## Community - Join the [Wix Developers Discord server](https://discord.gg/47gUT9KabP) and [forum](https://forum.wixstudio.com/) to ask questions, discuss complex topics, and collaborate with other Studio creators. - Contact [customer care](https://www.wix.com/contact) for support with platform issues, billing, or account questions. - Follow the Wix [changelog](https://dev.wix.com/docs/changelog.md) for up-to-date information about Wix features and APIs. - [Subscribe](https://www.wixforms.com/f/7394355343209268224) to the Wix newsletter to receive the latest updates and feature announcements. ## Next steps Start building or explore the resources below to go deeper: - Explore [the Wix Ecosystem](https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md) and [API Reference](https://dev.wix.com/docs/velo/articles/api-overview/introduction.md) to understand the platform and what you can build. - Browse [tutorials](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-re-captcha-to-protect-data-submission.md) and [code examples](https://dev.wix.com/docs/coding-examples.md) for step-by-step guidance on implementing functionality for a site. - Follow the [Quick Start](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md) to start building. Website development on Wix is transitioning to the JavaScript SDK. Learn how to [migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: The Wix Ecosystem ## Article: The Wix Ecosystem ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md ## Article Content: # The Wix Ecosystem Before writing code for a site, it's important to have a basic understanding of the greater Wix ecosystem. Once you understand what each part of the ecosystem does and how they work together, you'll be ready to start writing code that interacts with the various parts of the ecosystem. The Wix ecosystem includes a range of tools and services that enable you to build a web presence and manage your business online. The following diagram shows the main parts of the Wix ecosystem: ![Wix ecosystem diagram](https://wixmp-833713b177cebf373f611808.wixmp.com/images/45a8810238260dae1fdc35cebe2d786b.png) ## Site creation Wix offers multiple approaches for creating websites, each designed for different needs and technical preferences. ### Drag-and-drop editors Wix offers the following drag-and-drop editors: - [Wix Studio](https://support.wix.com/en/wix-studio): An advanced web creation platform for agencies, freelancers, and enterprises. - [Wix Editor](https://support.wix.com/en/using-the-wix-editor): Wix's classic site builder. These editors offer a visual, drag-and-drop interface with AI-powered features that lets Wix users: - Add and customize site pages, elements, widgets, and more. - Customize site functionality with code. - Manage site content including text and media. - Add and manage Wix business solutions, such as Wix Stores or Wix Bookings. - Add and manage apps built by 3rd-party developers. Sites built with these editors have a Wix-powered frontend. These are the sites documented in the [Develop Websites](https://dev.wix.com/docs/develop-websites.md) portal. Here's what Wix Studio looks like: ![Wix Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/602c65a5abe17eba3931fee886a3cbf8.png) ### Other site creation approaches While the Develop Websites documentation focuses on drag-and-drop editor development, Wix offers additional site creation approaches: - **[Wix Vibe](https://support.wix.com/en/article/wix-vibe-an-overview)**: A conversational website creation tool that builds sites through natural language prompts. While Vibe includes visual editing components, these aren't as fully-featured as the dedicated drag-and-drop editors. Sites built with Vibe take the form of Wix-managed Headless sites, with a React based frontend and use the Astro framework. - **[Wix Headless](https://dev.wix.com/docs/go-headless.md)**: Enables developers to build custom frontends using any technology while leveraging Wix's backend infrastructure. Sites built with Wix Vibe or Wix Headless are documented in the [Go Headless](https://dev.wix.com/docs/go-headless.md) portal. ## Site management Once you create a site, Wix provides tools for ongoing management and operation. ### Site dashboard The dashboard is the site's control center. Whether a site was built using the drag-and-drop editors, created with Vibe, or developed as a headless project, all sites use the same unified dashboard. Only you and your collaborators have access to the dashboard. Most dashboard functionality comes out of the box and enables you to set up, manage, and operate a site and its installed apps. You can also design and code custom dashboard pages. In the dashboard, you can: - Configure site settings. - Manage site content including text and media. - Manage payments, customers, online marketing, automations, and more. - Add and manage business solutions, such as Wix Stores or Wix Bookings. - Add and manage apps built by 3rd-party developers. - Monitor site metrics, such as SEO performance, sales reports, and site traffic. Here's what the dashboard looks like: ![Wix dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/46cca8591e446c4e032473b4c8b65301.jpeg) ## Development and integration Wix's platform offers an extensive array of services and tools for developers to integrate with and extend the Wix ecosystem. ### APIs Wix APIs give you access to Wix's services and resources, providing a gateway that enables you to easily integrate with different areas of the Wix ecosystem. APIs include: - **Business solutions**: Interact with Wix's business solutions, such as Stores, Bookings, Events, and Restaurants. - **Payments**: Manage payments with Pricing Plans, Payments, and Billing modules. - **CRM and Members**: Interact with and manage site contacts and members. - **Data**: Work with data stored in Wix-hosted and external databases. - **Automations**: Streamline work processes and send notifications. - **Web methods**: Create secure backend functions accessible from the frontend. ### CMS Wix's Content Management System (CMS) is where you manage a site's content, such as text, media, videos, products, and booking services. The CMS seamlessly integrates with Wix's site-building tools, providing a user-friendly interface for creating and editing content, as well as mechanisms for easily connecting CMS data to site elements. With Velo APIs, you can programmatically interact with CMS content. ### Wix business solutions At the heart of Wix's backend are its native business solutions, which cater to diverse industries with advanced solutions for bookings, online stores, events, payments, eCommerce, loyalty programs, and more. These apps built by Wix are designed to integrate seamlessly with Wix websites, as well as with external clients that take advantage of Wix's [headless](https://dev.wix.com/docs/go-headless.md) infrastructure. Velo provides dedicated modules for interacting with these business solutions. ### Apps When building a site, you can install apps built by Wix or 3rd-party app developers from the [Wix App Market](https://www.wix.com/app-market/) to add pre-built functionality to your site. #### 3rd-party apps Wix apps are packages of reusable functionality, created by 3rd-party vendors, that you can add to your site. They can contain anything from a simple site widget to a complete business solution, with backend logic, user interfaces, and database collections. Wix apps are listed in the [Wix App Market](https://www.wix.com/app-market/). You can also [build your own apps](https://dev.wix.com/docs/build-apps.md) to share functionality across multiple sites or to offer functionality you've built to other Wix users. ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md) - [Development Environments](https://dev.wix.com/docs/develop-websites/articles/get-started/development-environments.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Website Development Features ## Article: Website Development Features ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/website-development-features.md ## Article Content: # Website Development Features Wix offers a robust suite of features that support an efficient and productive development workflow. This overview covers the key features that help you develop, test, publish, and maintain custom sites with advanced functionality. ## Developer environment Leverage Wix's suite of tools and IDE options designed for efficient site development. **IDE options**: Choose from the code editor built into Wix Studio and the Wix Editor, the online Wix IDE, or a local IDE like VS Code. **Git integration and Wix CLI for Sites**: When working in a local IDE, use Git and the Wix CLI for Sites to manage version control and local development, allowing easy collaboration, change tracking, and branching. **Secrets Manager**: Store sensitive information securely with the Secrets Manager, which keeps API keys and other sensitive credentials hidden from your code. **Packages and apps**: Add functionality with npm packages, Velo packages, or custom apps tailored to your project’s needs. **AI Assistants**: Use AI-powered tools to speed up development, offering code suggestions, debugging support, and productivity enhancements. ## Build a custom backend Develop backend code, hosted by Wix, to build advanced features and integrations, all managed securely on the platform. **Web modules**: Write backend code that securely handles business logic and can easily be called from the frontend. **Recurring jobs**: Automate tasks, such as data updates and email notifications, to run on a regular schedule. **HTTP functions**: Expose your site functionality as a service by creating endpoints that external applications can call, opening up integration possibilities. **Routers**: Take full control of your site’s URL structure and routing by customizing how requests to specified paths are handled. **Event handlers**: Add backend event handlers to respond to actions that occur on your site, such as a new contact being created or a customer abandoning their cart. ## Build a custom frontend Enhance the pages you've designed in the Wix editor by adding custom code to create interactive and dynamic user interfaces, allowing you to fully tailor the user experience. **$w API**: The $w API lets you interact with UI elements. With support for over 100 element types-—-from simple elements like images, buttons, and input fields to advanced components like repeaters, galleries, and multi-state boxes-—-the $w API enables you to add interactivity, manage user input, and customize element behavior on the page. **CSS**: Use CSS to customize the appearance of your site—from colors and fonts to sizes and positions of elements. Work with the existing Wix CSS classes or create your own custom classes. **Custom elements**: Create custom HTML elements and integrate them seamlessly into your site, enabling unique functionality and design beyond what the built-in components provide. ## Data Manage your site's content and data with Wix's integrated database solutions, allowing for dynamic, data-driven web experiences. **Wix CMS**: Manage structured content with the Wix CMS, allowing you to store, query, and display data programmatically or using no-code features. **External databases**: Connect to external databases, so you can integrate data from other sources using the same APIs and no-code features that you use with native Wix data. **Dynamic pages**: Use dynamic pages to create multiple pages from a single design by connecting page elements to database content. Use the Dataset API to programmatically fine-tune the functionality of dynamic pages. **Data API**: Access and manipulate CMS and external content programmatically with the Data API, enabling you to create, read, update, and delete data directly from your code. ## Wix business solutions Leverage Wix's robust suite of business solutions to incorporate features such as eCommerce and bookings, while having the flexibility to extend and integrate with these solutions to fit your business needs. **Wix business solution APIs**: Integrate directly with Wix’s business solutions, such as Wix Bookings and Wix Stores, using dedicated APIs to access and manage bookings, orders, and more. **Service plugins**: Enhance Wix business solutions by creating custom service plugins that inject your own custom logic or integrate 3rd-party services into Wix flows. For example, you can create a service plugin to customize price calculations for services booked using Wix Bookings or to add an unsupported payment provider as an additional payment option. **Replace business solution pages**: Customize the user experience by replacing default Wix business solution pages with your own, giving you full control over design and functionality. For example, you can replace the default Wix Bookings service page with your own custom version. Your page will appear in the booking flow instead of Wix’s default page. ## Test your site Ensure your site performs optimally and provides a seamless user experience by utilizing Wix's testing features to identify and rectify issues before going live. **Preview**: Use preview mode in the editor to test your code in a controlled environment. You can interact with a preview version of your site and see errors and logs in the developer console, without affecting the live version of your site. **Functional testing**: Run tests on backend methods in isolation to ensure they work as expected, without having to write extraneous testing code. **Test sites**: Create a dedicated test site to experiment with features, test integrations, and refine functionality without impacting your live site. This is especially useful for complex updates or collaborative testing. ## Maintain your site Keep your site updated and running smoothly with Wix's maintenance tools, ensuring long-term performance and security. **Monitoring**: Keep track of your site’s performance, monitor your storage usage, and troubleshoot site issues with the monitoring dashboard. Keeping tabs on monitoring data allows you to proactively address issues and ensure a smooth user experience. **Logs**: Access detailed logs to review site activity, debug issues, and gain insights into user interactions. You can view logs in real time within Wix and connect to Google Cloud Logs to store logs and perform analysis, generate visual depictions of log data, and to extract meaningful insights from your logs. ## What’s next - [Quick Start](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md) - [The Wix Ecosystem](https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md) - [Development Environments](https://dev.wix.com/docs/develop-websites/articles/get-started/development-environments.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Development Environments ## Article: Development Environments ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/development-environments.md ## Article Content: # Development Environments When writing code, having the right development environment can greatly enhance your productivity. Wix provides development options that let you work directly within Wix or work with the tools you already use. The development environments are: - The built-in [code editor](#code-editor) in Wix Studio or the Wix Editor. - The online [Wix IDE](#wix-ide) for Wix Studio sites. - A [local IDE](#local-ide) of your choice, such as VS Code, when using Wix Studio or the Wix Editor. You can use a local IDE, in conjunction with the Wix CLI for Sites, after connecting your site to GitHub. The development environment you should choose depends on your project needs and workflow. Each option offers unique advantages, and you can mix and match environments to some extent as needed. > **Note**: Some Wix website development features are only available in specific development environments. See each feature’s documentation for a list of development environments that support it. ## Code editor The built-in code editor is a browser-based environment available directly within the Wix Studio or Wix Editor. It provides an easy way to write and manage code alongside the visual site editor, enabling you to write code without leaving the Wix platform. ![Code editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/edd17d777e317076582b7df9b22178ed.png) Learn more about the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). ## Wix IDE The Wix IDE is an advanced, VS Code-based online development environment available for Wix Studio sites. The Wix IDE opens in a separate browser tab or window with hot reloading. This gives you more space for both coding and site editing. Having one window to design and preview and a second window to code is especially useful if you’re working with a multi-monitor setup. The Wix IDE also enables concurrent editing, allowing multiple developers to work in the Wix IDE at the same time. When working in the Wix IDE, you can utilize the [Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), an intelligent companion that offers real-time coding assistance. ![Wix IDE](https://wixmp-833713b177cebf373f611808.wixmp.com/images/80f327c068d3c46954bf4d585e6b3b1d.png) Learn more about the [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md). ## Local IDE Use a local IDE, such as VS Code, for the flexibility and power of a full-featured development environment on your own machine. With a local setup, you can customize your environment and use the extensions and shortcuts that you use for all your non-Wix projects. To support local development, use the Wix CLI for Sites, which connects your local code to your site through GitHub. Once you connect a site to GitHub, you can only edit the site’s code in your local IDE. The CLI, along with a local editor, allows you to write, test, sync, and publish your code from your local environment. Because your code is stored in a Git repository, you can manage branches, versions, and team collaboration just like you would for any other Git project. ![Local IDE](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8c45c4e13c906cf619e1d4a0eb69565e.png) Learn more about using a [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md#code-with-velo-in-your-local-ide). ## What’s next - [Quick Start](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md) - [The Wix Ecosystem](https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md) - [Website Development Features](https://dev.wix.com/docs/develop-websites/articles/get-started/website-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate with Wix's Business Solutions ## Article: Integrate with Wix's Business Solutions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/integrate-with-wix-s-business-solutions.md ## Article Content: # Integrate with Wix’s Business Solutions Wix offers a range of powerful business solutions, enabling you to sell products and services, run events, write blogs, manage restaurants, and more—all seamlessly integrated with your website. These solutions are built to work out-of-the-box on Wix sites, but with custom code, you can extend their capabilities and tailor them to fit your specific needs. Your site can integrate with Wix’s business solutions in the following ways: - [Wix business solution APIs](#wix-business-solution-apis): Leverage functionality from Wix’s business solutions like Wix Stores and Wix Bookings. - [Service plugins](#service-plugins): Inject custom functionality or 3rd-party integrations into Wix business solutions. - [Replace business solution pages](#replace-business-solution-pages): Customize user experience by replacing Wix’s default business solution pages with your own. ## Wix business solution APIs Wix business solution APIs allow you to programmatically interact with Wix's business solutions, such as Wix Stores and Wix Bookings. This enables you to extend their capabilities, integrate with external systems, and create custom functionality tailored to your site's specific requirements. For example, you can use Wix business solution APIs for: - **Personalized recommendations**: Use customer data to generate personalized Wix stores product recommendations on your site. - **Custom service availability UI**: Retrieve and display the availability status of Wix Bookings services in a custom UI. - **Customer data sync**: Sync Wix Member profiles and purchasing history with data from an external CRM system for a unified customer view. ## Service plugins [Service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md) allow you to inject additional functionality or integrate 3rd-party services directly into Wix’s business solutions. Plugins consist of functions that are called automatically by Wix apps at defined points in their flows. These functions receive and return data in specific formats, enabling seamless integration with the Wix app’s business logic. For example, you can use service plugins for: - **Custom shipping rate**: Integrate 3rd-party logistics services to provide customized shipping rates in Wix Stores. - **Custom bookings pricing**: Implement dynamic pricing models for services in Wix Bookings, such as surge pricing or member-only discounts. - **Custom payments provider**: Integrate a 3rd-party payment gateway to offer additional payment options for customers in Wix Stores. ## Replace business solution pages Customize the user experience by replacing default Wix business solution pages with your own. Design a page and add functionality to it using the $w API for page elements and the Wix business solution APIs. When the business solution flow reaches the predetermined step, your page is presented to site visitors instead of the usual Wix page. Once visitors finish interacting with your page, you redirect them back to the next Wix page in the flow. For example, you can replace the following pages with your own versions: - **Bookings service page**: Replace the default Wix Bookings service page with a custom-designed page that includes unique features, such as custom booking options and additional information specific to your services. - **Stores product page**: Replace the default Wix Stores product page with a custom version where you can showcase your products with enhanced visuals, custom layout, and unique interactive elements to improve the shopping experience. - **Custom plans & pricing page**: Replace the default Plans & Pricing page from the Wix Pricing Plans app with a custom-designed page that highlights your pricing plans with a unique layout and interactive pricing details to better attract and convert visitors. ## What’s next - [Quick Start](https://dev.wix.com/docs/develop-websites/articles/getting-started/quick-start.md) - [Integrate with 3rd-Parties](https://dev.wix.com/docs/develop-websites/articles/getting-started/integrate-with-3rd-parties.md) - [Website Development Features](https://dev.wix.com/docs/develop-websites/articles/getting-started/website-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate with 3rd-Parties ## Article: Integrate with 3rd-Parties ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/integrate-with-3rd-parties.md ## Article Content: # Integrate with 3rd-Parties Integrating 3rd-party services into your site allows you to save time, extend functionality beyond native features, and connect seamlessly with existing systems you have already invested in. Your site can integrate with 3rd-party solutions in the following ways: - [Fetch API](#fetch-api): Connect with 3rd-party APIs securely. - [npm packages](#npm-packages): Leverage 3rd-party functionality within your Wix site. - [HTTP functions](#http-functions): Expose your site's functionality so 3rd-parties can call your site’s APIs. - [Service plugins](#service-plugins): Inject 3rd-party services into Wix business solutions. ## Fetch API The [Fetch API](https://dev.wix.com/docs/velo/api-reference/wix-fetch/introduction.md), an implementation of the standard JavaScript Fetch API you're likely already familiar with, enables you to connect your site to 3rd-party APIs. Store sensitive credentials, such as API keys, in the Secrets Manager to keep your API calls secure. For example, you can use the Fetch API for: - **Weather updates**: Display live weather updates by fetching data from a weather service API. - **Inventory sync**: Keep your store inventory synced with a 3rd-party inventory management system. - **Analytics**: Send custom event data to an analytics service. ## npm Packages With [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) you can leverage a vast array of prebuilt 3rd-party modules to extend the functionality of your site. By integrating these packages, you can easily add complex features without having to build them from scratch. For example, you can use npm packages for: - **PDF generator**: Use a PDF generator module to dynamically create and customize PDF documents on your site. - **SMS messaging**: Send SMS notifications to visitors for order confirmations, appointment reminders, or promotional messages. - **QR code generation**: Integrate a module to dynamically generate QR codes for promotional offers or mobile app links. ## HTTP Functions With [HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/introduction.md) you can expose your site as a service and allow 3rd-party systems to interact with your site data and functionality. For example, you can use HTTP functions for: - **Webhooks**: Set up an HTTP function to receive and process webhooks from external services, such as payment notifications or delivery updates. - **Alternative frontends**: Use HTTP functions to expose the logic and data of your site to alternative frontends such as mobile apps, smartwatch apps, or other IoT devices. - **Automation services**: Integrate your Wix site with automation services. You can create endpoints that automation platforms can trigger as part of an automation workflow. ## Service plugins [Service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md) allow you to inject additional functionality or integrate 3rd-party services directly into Wix’s business solutions. For example, you can use service plugins for: - **Custom shipping rate**: Integrate 3rd-party logistics services to provide customized shipping rates in Wix Stores. - **Custom bookings pricing**: Implement dynamic pricing models for services in Wix Bookings, such as surge pricing or member-only discounts. - **Custom payments provider**: Integrate a 3rd-party payment gateway to offer additional payment options for customers in Wix Stores. ## What’s next - [Quick Start](https://dev.wix.com/docs/develop-websites/articles/getting-started/quick-start.md) - [Integrate with Wix Business Solutions](https://dev.wix.com/docs/develop-websites/articles/getting-started/integrate-with-wix-s-business-solutions.md) - [Website Development Features](https://dev.wix.com/docs/develop-websites/articles/getting-started/website-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Quick Start ## Article: Getting Started with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md ## Article Content: # Quick Start With Wix you can easily extend your site’s functionality using code. In this tutorial, we walk you through the essential steps to get you quickly up and running with code. **You’ll learn how to:** - Enable coding in the editor - Select a UI element in your code - Add a code interaction - Use a Wix API - Preview your code in action - View console logs We added a text element and button to our site, and then used Wix APIs to change the element’s text when the button is clicked. To get started, watch the video tutorial, or follow the steps below. ## Before you begin Sign up for a [Wix Studio account](https://www.wix.com/studio) and log in. ## Step 1 | Enable coding To start coding in your site, you need to enable Wix developer mode. In the left sidebar, click ![Code icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2608ab9f3b2daf53dcf1db19f1b5de11) and then **Start Coding**. Wix Studio start coding This sets up your developer environment which includes a built-in code editor (IDE) at the bottom of your page, and a Code sidebar with advanced developer functionalities such as access to your site’s page code, backend and public code files, databases, code packages, and other tools and features to extend your development experience. ## Step 2 | Add elements to a page In this step, we add elements to the Home page of our site, and change the default text of the elements using the UI. To do this: 1. In the left sidebar, click Add Elements ![Add icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/89c52700122a42d8b153811db8e5f330). 2. Add the following elements to your site: - Text - Button 3. Use the UI to change the default text of the elements to whatever you like. We changed the text of our button to 'Click Me', and our text element to 'Hello World!'. Later in the tutorial we show how to change the text of our text element using code. ## Step 3 | Select UI elements in your code To select these UI elements in your code, do the following: 1. Click the button element. You can now see the button element’s ID in the Properties & Events panel next to the built-in code editor. 2. Change the button’s ID. We call ours 'btnClick'. It’s best practice to change all of your element IDs to more descriptive text so that you can easily identify them when you start writing code. We also changed our text element ID to 'txtMessage'. 3. To use the elements in our code, we need to look at the [Wix API Reference](https://www.wix.com/velo/reference/api-overview). The reference contains various APIs to interact with site elements, your site’s database content, Wix business solutions, and more. In our example, we use the [$w](https://www.wix.com/code/reference/$w.html#$w) selector function to select elements on a page by ID or by type. We select our elements by ID as follows: - Button: $w('#btnClick') - Text: $w('#txtMessage') > **Note:** To access and modify elements on your site, you must use the $w selector function. You can't use Web APIs or 3rd party libraries such as jQuery to access or manipulate the DOM. ## Step 4 | Add an event handler Now that you know how to select UI elements in your code, let’s add an [`onClick()`](https://www.wix.com/velo/reference/$w/button/onclick) event handler to the button. Events run when they are triggered by a specific action on their element. In our example, every time the button is clicked, the event’s code will run. **To add the event handler:** 1. Go to the code editor at the bottom of your page. Notice the [`onReady()`](https://www.wix.com/velo/reference/$w/onready) function in your page code. We write the code for our event handler inside this function since it runs when all the page elements have finished loading. ![Code icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a001d4b08e0124f096962be650bb9ff6) 2. In the `onReady()` function, add the button element using the $w function and the element ID we gave it earlier, $w('#btnClick'). Notice that when you start typing '$w', you can see a list of all the available elements. This is useful to be able to find your target elements, especially as your site grows. 3. Attach the `onClick()` event handler to the selected button element. ```js $w.onReady(function () { $w("#btnClick").onClick(() => {}); }); ``` ## Step 5 | Define the code in the event handler Now that we have a button with an `onClick()` event handler, we need to define the code we want to run when the button is clicked. In our example, we want to change the text of the text element when the button is clicked. To define the code we want to run when the button is clicked, do the following: 1. First add a console log in the `onClick()` event handler function to test that it is working. ```js $w.onReady(function () { $w("#btnClick").onClick(() => { console.log("clicked!"); }); }); ``` 2. Now let’s run the code in Preview mode to test if it is working. Click **Run** on the top right of the code editor. Once in Preview mode, click your button. If everything is working as expected, you’ll see our console log, 'clicked!' printed in the Developer Console at the bottom of the page. 3. Now that our button’s event handler is working, we define our event. In our case, we want the text element’s text to change from the current 'Hello World!' to 'Hello from Wix Studio!' when the button is clicked. To do this, we select our text element, $w('#txtMessage'), and then add the Text API’s `.text` property: ```js $w.onReady(function () { $w('#btnClick').onClick(() => { console.log('clicked!'); $w('#txtMessage').text = 'Hello from Wix Studio!’; }) }); ``` ## Step 6 | Preview your code in action Before publishing our site, let’s see a preview of our code in action. 1. To enter Preview mode, you can either click the **Run** button like we did in the previous step, or click the Preview icon ![Preview icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d867dbfaf7b198120493f1264afa27e) in the top right corner of the editor. 2. In preview mode, click the button. The text element’s text will change to 'Hello from Wix Studio!', and you’ll see our console log, 'clicked!' printed in the Developer Console at the bottom of the page. Your code is up and running, and you’ve now learned the basics of how to code with Wix. ## Next Steps Learn more about coding with Wix: - **Customize your site’s UI:** Interact with site elements and add custom behaviors with the [$w() API](https://www.wix.com/velo/reference/$w). - **Add advanced backend functionality:** Write server-side code on your site's backend, and [call the backend code from your frontend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md). Then test your backend code in the code editor using [Functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md). - **Integrate with external resources:** Add [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md), and [access 3rd-party services with the fetch API](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/fetching-services/accessing-3rd-party-services-with-the-fetch-api.md). - **Manage your site data:** Use the [Data API](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md) to work with your database collections using code. Use the Wix CMS, or integrate with popular external databases such as MySQL, Google Cloud, and AWS. - **Start with pre-built examples:** Use the [Get Started with an Example](https://dev.wix.com/docs/develop-websites/articles/getting-started/get-started-with-an-example.md) feature in Wix Studio to access a page with pre-built code snippets, helping you begin coding and explore Velo's basic features. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Get Started with an Example ## Article: Get Started with an Example ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/get-started-with-an-example.md ## Article Content: # Get Started with an Example > **Note:** This feature is only available in Wix Studio and is not yet available to all Wix Studio users. Wix Studio provides a pre-built example page that demonstrates some of Velo's features: - **Text Manipulation**: Set and change text content dynamically. - **Element Visibility**: Show and hide page elements. - **Expandable Sections**: Create and manage foldable content areas. This article explains how to access and use this example page. ## Access the example page To open the example page with code snippets: 1. Open a Wix Studio site that you haven't added code to yet. 2. In the left sidebar, click the Code icon ![Code icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2608ab9f3b2daf53dcf1db19f1b5de11.png) and then **Start with an example**. A new page named "Get Started with Code" is created. > **Tip**: If you've already enabled coding, you can access the example page by clicking ![Code icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2608ab9f3b2daf53dcf1db19f1b5de11.png) then **Help > Getting Started** in the left sidebar. Get Started with an Example ## Use the example page The "Get Started with Code" page demonstrates basic Wix Velo functionality. Here's how to get the most out of it: 1. **Review the examples**: The page features several interactive demonstrations. 2. **Test the behavior**: Preview to see how each example works. 3. **Study the code**: Examine the snippets associated with each example. 4. **Modify and experiment**: Change the code and observe the effects. 5. **Preview your changes**: Check the results after modifying the code. ## Try an example To see an example in action: 1. Go to the "Get Started with Code" page. 2. Choose an example from the page, such as the "Change text on button click" example. The following screenshot shows this example: Get Started with an Example 3. In the code editor, review the existing code for the example. 4. Modify the code: - Find the line that sets the text for the button. - Change the text inside the quotation marks. 5. Click the preview icon ![Preview icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d867dbfaf7b198120493f1264afa27e.png) in the top right corner to see your changes. 6. Click the blue "Click to Change Title" button. The existing text changes when you click the button. ## Next steps After exploring the examples: - Use these examples as inspiration for your own Wix Studio site. - Explore the [Wix Velo API Reference](https://dev.wix.com/docs/velo.md) for all API functionality. - Visit the [Wix Coding Examples Site](https://dev.wix.com/docs/coding-examples.md) to see even more examples. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Wix Site MCP ## Article: About the Wix Site MCP ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/about-the-wix-site-mcp.md ## Article Content: # About Wix Site MCPs Every Wix site has its own [built-in](https://support.wix.com/en/article/ai-tools-connecting-wix-sites-to-ai-platforms) MCP (Model Context Protocol) server. Anyone can connect their compatible AI agent to the MCP server of any Wix site. This allows users and site visitors to interact with Wix sites through AI, using natural language requests. The site MCP server makes it easy for visitors to: - Get business details like contact information and location. - Discover the products and services the business has to offer. - Book services and make reservations. - Start a purchase process and be directed to the site to complete checkout. - Learn about a business by getting relevant answers to questions, instead of having to search the site. ## Connect to the MCP server Every Wix site has a unique endpoint, but they all use the following format: ```shell /_api/mcp ``` In your code, replace `` with the domain of the site you want to connect to. For example, if you want to communicate with the MCP server of `https://andream.wixsite.com/andreas-kitchen`, the endpoint is: ```shell https://andream.wixsite.com/andreas-kitchen/_api/mcp ``` ## Make API calls to Wix site MCPs Make client requests as POST calls that follow MCP [architecture](https://modelcontextprotocol.io/docs/learn/architecture). You don’t need to include authentication for site MCPs. For example: ```shell curl -X POST "https://example.wixsite.com/example-store/_api/mcp" \   -H "Content-Type: application/json" \   -d '{     "jsonrpc": "2.0",     "method": "tools/call",     "id": 1,     "params": {       "name": "SearchInSite",       "arguments": { "searchTerm": "list available products" }     }   }' ``` ## Available tools Wix site MCP servers make the following tools available to MCP clients to interact with a site. ### ReadFullDocsArticle Fetches a complete article from the [Wix developer documentation portal](https://dev.wix.com/docs/develop-websites.md). Parameters: | | | | ------------ | ------------------------------------------------------------------------------------------------------- | | `articleUrl` | A string in `uri` format that contains the URL of an article in the Wix developer documentation portal. | ### ReadFullDocsMethodSchema Fetches the full schema for a Wix API method. The client should call this before calling the actual method. Parameters: | | | | ------------ | ------------------------------------------------------------------------------------------- | | `articleUrl` | A string in `uri` format that contains the URL of a reference article for a Wix API method. | ### CallWixSiteAPI Calls API methods on a site in order to perform actions on a site visitor’s behalf, such as querying site data or booking an appointment. Parameters: | | | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `visitorToken` | Visitor access token. If you have this in context, always use it and do not create a new one. If you don’t have it in your context, use the [`GenerateVisitorToken`](#generatevisitortoken) tool to get it. | | `url` | A string in format `uri` that contains the URL of the API method to call. Retrieve this URL using the [`SearchSiteApiDocs`](#searchsiteapidocs) tool or from the conversation context. Must be an absolute URL. Typically starts with `https\://www\.wixapis.com…` | | `method` | The HTTP method to use for the API call. | | `body` | A string representing the request body as a valid JSON object. | ### GenerateVisitorToken Creates a new visitor session and obtains a visitor access token for the site. If you don’t have a visitor token yet, use this tool before making a `tools/call` request with [`CallWixSiteAPI`](#callwixsiteapi). ### SearchSiteApiDocs Retrieves the API documentation for the Wix business solutions installed on a site, and informs the client on how to use the APIs. Use this tool for querying products and services on a site (instead of [`SearchInSite`](#searchinsite)). Parameters: | | | | ------------ | --------------------------------------------------- | | `searchTerm` | The term to search for the site API documentation.  | ### GetBusinessDetails Retrieves business and site details such as timezone, email, phone, address, etc. ### SearchInSite Searches the site for information. Parameters: | | | | ------------ | ----------------------------------- | | `searchTerm` | The term to search for in the site. | You can also make a tool discovery request from your client to retrieve the list of tools.  ```shell curl -X POST "https://example.wixsite.com/example-store/_api/mcp" \   -H "Content-Type: application/json" \   -d '{     "jsonrpc": "2.0",     "id": 4,     "method": "tools/list"   }' ``` The `tools/list` call returns the name, description, and input schema of each tool. > **Note**: The MCP server notifies your client when the tools are updated. To receive the latest tool updates, make sure to implement a `tools/list` call upon receipt of a tool update notification. ## See also - [The Wix MCP for developers](https://dev.wix.com/docs/sdk/articles/use-the-wix-mcp/about-the-wix-mcp.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Wix Harmony ## Article: About Wix Harmony ## Article Link: https://dev.wix.com/docs/develop-websites/articles/get-started/about-wix-harmony.md ## Article Content: # About Wix Harmony Wix Harmony is a new Wix Editor that offers a flexible, intuitive platform for building sites. It brings together advanced AI tools, including Aria for prompt-based creation, paired with freeform drag-and-drop capabilities. Harmony is designed for self-creators who want an AI-assisted site building experience without code. Wix Harmony doesn't support site code. If your projects require code customization, continue using [Wix Editor](https://www.wix.com) or [Wix Studio](https://www.wix.com/studio). However, you can use [custom code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/custom-code/about-custom-code.md) to add HTML, JavaScript, and CSS snippets to Harmony sites. > **Note:** If you're building apps with Wix Blocks, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md). ## Existing sites Existing Wix sites with site code will continue to work normally. You can still access and edit your sites and code through the Wix Editor or Wix Studio. Existing sites are not being migrated to Harmony. ## Build with Wix Studio For projects that require site code, Wix Studio is your development platform. Wix Studio supports: - Full coding capabilities with the Wix JavaScript SDK and Velo APIs. - Pixel-perfect layouts with responsive design systems. - Collaboration tools for working with team members. - Professional tooling including IDE integration and Git workflows. Learn more about [Wix Studio](https://www.wix.com/studio). ## Features not available in Wix Harmony Features not available in Wix Harmony include: - [Custom site code](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md), including page code and backend code - [Custom CSS](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md) - [HTTP functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md) - [Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) - [Data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) - [Backend event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) - [Web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) - [Scheduled jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md) ## What's next We're working on bringing the AI creation experience to Wix Studio. The new features aim to give you the best of both worlds: powerful AI tools combined with full development capabilities. Stay tuned for updates. ## Get support We're here to answer your questions and concerns. [Contact us](https://dev.wix.com/docs/develop-websites/articles/contact-us/contact-us.md). ## See also - [About Developing Websites](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) - [About Wix Harmony and Apps](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md) - [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md) - [About Custom Code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/custom-code/about-custom-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Studio: Working with the Code Panel ## Article: Wix Studio: Working with the Code Panel ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md ## Article Content: # Wix Studio: Working with the Code Panel
__Note:__ The features and workflows listed below are only available in Wix Studio.
The Code panel is where you can write code to add custom functionality and interactions to your site. Use the Code panel to add frontend code and CSS styling directly with the built-in code editor, manage your collections, add code packages, and add custom apps or extensions. You can also use the panel to access the platforms, such as the Wix IDE or GitHub integration, for advanced coding features like backend web modules, data hooks, and http functions. To begin coding, select the code icon ![code icon](https://github-production-user-asset-6210df.s3.amazonaws.com/95754543/253806250-18a75e8d-67e5-4b68-958c-ae320e0c972e.png) and click **Start Coding**.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/098e4cea05a3ba60a4c0af3847a076dd.png)
## Code Editor The code editor is where you write and edit the code for your site. You can toggle between different code files in the Page Code and Public & Backend sections of the Code sidebar. ### Page Code The Page Code section opens the frontend code editor for every page on your site. ![Page Code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0d3719214176dd9aa9423c7257ab0331.png) Toggle between your site pages in the Page Code section of the Code sidebar, in the Pages panel, or in the top bar to edit each page's frontend code (shown below). You can see which page you are currently editing at the top left of the code editor.
### Public Code The Public section is where you put JavaScript files that contain the code you want to use on any page in your site. For example, you may have a function that you need to use in multiple pages and you don't want to repeat it on each page. You can write that function once in a public file and then call it from any page, or from any backend or public file as needed. You can add public code in the Public & Backend section of the Code sidebar. ### Backend Code The Backend code section of the Code sidebar includes files that are not publicly accessible from your site. Since the backend code isn't visible, you can place sensitive information in your backend code that might otherwise be a [security risk](https://support.wix.com/en/article/velo-security-best-practices#code-visibility). You can add backend code in the Public & Backend section of the Code sidebar, or with one of these options: + [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide): Click **Code in Wix IDE** at the top of the code editor to get started. + [GitHub integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md): Navigate to the GitHub section to get started. >**Note:** > Currently, you can't write code using both Wix IDE and GitHub integration. ### Global Code Run your global code in the **masterPage.js** file, located in the Page Code section of the Code sidebar. Use this file to add frontend code that runs on every page. If you want to add processing for your header or footer, this is the place to do it. This is also the place to handle elements that you want to put on every page, like a search bar or shopping cart icon for a store. >**Note:** > Don't import functions from **masterPage.js** into the code files of individual pages. Doing this will cause the masterPage onReady function to run twice on that page. If there's a function that you want to use in the code for multiple pages but not every page, store it in a **public.js** file and import it from there. Learn more about the public files section in [About the Wix IDE](https://support.wix.com/en/article/about-the-wix-ide). `youtube:https://www.youtube.com/watch?v=442XHD7-Ah4` ### CSS The **CSS** section, located under the Page Code section of the Code sidebar, allows you to add CSS styling that applies globally to every page of your site. Learn more about [CSS editing](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md). ![global.css](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9247162de368bd6c33fb44b2cc6c3c43.png) ### CSS Classes Panel In addition to global CSS styling, you can add CSS styles to specific elements on your site with the CSS Classes panel. Select a specific element on your site and the CSS Classes panel displays the available Global classes for that element. You can also create your own custom class with this panel. See [custom CSS classes](https://www.wix.com/velo/reference/$w/styling-elements-with-css#$w_styling-elements-with-css_available-classes) to learn more.
### Properties & Events Panel Use the Properties & Events panel to [work with selected elements](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md), such as the header and footer, and [datasets](https://support.wix.com/en/article/about-datasets-and-connecting-data). With the Properties & Events panel you can change an element's ID and default values, or [add an event handler that triggers](https://support.wix.com/en/article/velo-reacting-to-user-actions-using-events) when an action is taken on this element. Note that not all actions are available for every element. Select an element on your site and click the Properties & Events tab in the Code panel:
#### Available actions in the properties & events panel: + **Change an element's ID:** The ID field holds the name of the element you selected. Initially a default name is assigned to your element, but you can rename the element to something meaningful. + **Change an element's default values:** By default, an element on your page will have certain initial states. For example, a form might be hidden when your page is loaded, or a button might be disabled. You can change these default values. + **Add an event handler:** Click the event that you want as a trigger and the function for the event handler is added to your page code. Then add the code to run with this event. ### Right-Click Menu You can right-click anywhere in the code editor to open the [right-click menu](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-with-the-code-editor-toolbar-and-right-click-menu-with-wix-editor.md) and display more options.
![right click menu](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/3eeeab8e-2074-45f7-ae46-10d01af1b8e4/bf4a8fdb-e2a3-4449-a7c4-ae873728084c.png)
### Format Code You can easily format your code by clicking on the **Format code** button in the code editor toolbar. You can also format your code using the "Format code" keyboard shortcut(Alt + Shift + F) or by clicking **Format** in the right-click-menu. ![format code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/cd5ca3644092c52df4e1ea874e4cbb99.png) >**Notes:** > - See the full list of [keyboard shortcuts](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/keyboard-shortcuts.md) you can use while editing your code. > - The "Search in all files" keyboard shortcut is not yet supported in Wix Studio. ## Velo Syntax and Autocomplete ### Selecting a Specific Element Velo lets you code using standard JavaScript. It also has a specific syntax, or set of rules, for selecting an element on your page, which is: ```javascript $w('#elementID') ``` If you know jQuery, this should look familiar. If you don't, here's what you need to know. **To select an element:** 1. Type $w. 1. Enclose the ID of the element in parentheses and quotes.  1. Add a hashtag before the element ID. >**Note:** > You can use either single quotes or double quotes. To make things even easier, Velo includes code completion. When you type the $, a pop-up window opens listing the elements on your page and the relevant Wix APIs.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/030571df-9516-496b-bc2a-2a8af88f13dd/2022/04/05/c215802a-23ce-41cb-8dc8-25168dcb9afd/5dcf71e1-bc78-4c48-a192-6a371302a21a.png)
Select the element you want. The reference to the element is added to your code with all the necessary syntax. 
**Tips:** * You can press Ctrl+space at any time to view the code completion pop-up. * Element IDs are case sensitive. '#Button1' is not the same as '#button1'. * If you add a new .js file in your page code, code completion lists the elements from the last page you were on.
You can find the ID of any element by hovering over it or selecting it. You can change the ID of any element in the Properties & Events panel. #### Selecting Multiple Elements If you want to select multiple elements by ID, use the same syntax as above to refer to your elements and separate each element with a comma, like this: ```javascript $w('#elementID1, #elementID2, #elementID3') ``` #### Selecting all Elements of a Specific Type To select all the elements of a specific type, use the ID of the element type without the hashtag, like this: ```javascript $w('Button') ``` The ID of the element type is the name of the element as it appears in the [Velo API](https://www.wix.com/velo/reference/$w.html).  #### JavaScript Templates In addition to autocomplete that relates directly to Velo, the Code panel also includes autocomplete for standard JavaScript templates and keywords. For example, if you type the word "for," the autocomplete list includes templates for "for statements" as well as the keyword "for." Each template includes a link to a standard JavaScript API where you can read more information.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/030571df-9516-496b-bc2a-2a8af88f13dd/2022/04/05/cde8b78b-cf82-4233-aa42-754e57c5476d/e0e19da3-f80e-4e66-b36c-20d6e95925d7.png)
When you select a JavaScript template, the full syntax for the template is added to the Code panel. For example, if you select the "for statement," the following template gets added to your code: ```js for (let index = 0; index < array.length; index++) { const element = array[index]; } ``` ## Working with Elements in the Code Editor When a page loads in a browser, it's possible for the code on the page to run before the page finishes loading. This can cause an error if your code tries to reference an element in the page before it's loaded. All the elements on your page should load before you try to access them using code. Make sure to include all code that uses the `$w` selector in the following function: ```javascript $w.onReady(function() { //TODO: write your page related code here... }); ``` This is only required if you add code on your own using the `$w` selector. Any code you add to a function using the Properties & Events panel runs only after the page loads. Learn more about `$w.onReady` [here](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md). All of the elements in the code editor have **properties**, **methods**, and **event handlers** that you can use to work with your elements and add functionality to your site. Add a period after the `$w` selector to see the full list of these items.
![auto complete](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9895f3e6640213bdcb360c893ad7c3fd.png)
Once you select and item, the necessary syntax is added to the end of your selector. You can move through the options using the up and down arrow keys to see a brief description of the functionality. Click the "Read more" link for more information.  ### Properties Properties contain information about your elements. Some of these are read-only, while others have values you can also set. For example, the text element has an `isVisible` property that returns whether the element is actually visible on-screen. This property is read-only. The text element also has the `text` property that contains the current text in the text element. This is a property you can both read and set. ### Methods Methods perform actions on your elements. For example, the button element has a `hide` method that causes the button not to appear on your site. Some methods have additional options that affect how the action occurs. For example, you can add animation to the `hide` method by specifying it in the parenthesis, like this: ```js $w("#button1").hide("fly"); ``` Learn more about methods in the [Velo API reference](https://www.wix.com/velo/reference/$w.html). ### Event Handlers [Event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) let your elements respond to user actions (events). When you add an event handler to an element, you need to specify an action when the event occurs. Do this in the **callback function** for your event. For example, let's say you have a button that says "Take the Tour" on it. You want to add functionality so that when a visitor hovers over the button the text changes to "Let's Go!". You would add code to your site that looks like this: ```javascript $w("#button1").onMouseIn(()=> //onMouseIn is the event handler. // The callback function starts here. { $w("#button1").label = "Let's Go!"; //This is the code that runs when the event occurs. } // The callback function ends here. ); ``` ### Warnings and Errors As you write your code in the Code panel, you may see warning and error indications in the form of a red or yellow wavy line beneath the relevant code. To view a warning or error message, hover over the underlined the code.  - **Warnings:** A warning in your code is an informational message that brings your attention to some code that you might want to change. Warnings do not stop your code from running and can often be safely ignored. Warnings are indicated by a yellow wavy underline. ![code warnings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9182cb1fe369aaf4e410bc0bfbd445fe.png) A common warning message occurs when you have an unnecessary 'else' after 'return'. This most often occurs when you use the following coding pattern: ```js function myFunction() { if(someCondition) { //do some stuff return 0; } else { //do other stuff return 1; } } ``` If `someCondition` is true, the function will return. That means that we don't need the `else` to stop the code from executing when `someCondition` is `true`. You can safely ignore this warning or change your code to the following pattern: ```js function myFunction() { if(someCondition) { //do some stuff return 0; } //do other stuff return 1; } ``` - **Errors:** An error in your code means that your code will not function properly. Depending on the type of error, your code will either not work as expected or not run at all. Make sure to fix all errors in your code before publishing your site for your site visitors to use. Errors are indicated by a red wavy underline. ![code errors](https://wixmp-833713b177cebf373f611808.wixmp.com/images/06c4afcd3aca9c6aa0ccc1eeb8c6a79a.png) Here are some common situations where you might find errors in your code: * Error message: "#text1" is not a valid selector (see image above) If you change the ID of an element that you are using in some of your page code, the `$w()` selections in your code will cause errors. For example, if you have a text element with the ID **text1** and you change the ID to **statusMessage**, all instances where you selected the element using `$w('#text1')` will now be errors. Note: you can use the Search and Replace functionality to fix this error throughout your code. * Error message: 'import' and 'export' may only appear at the top level When you import an API module, the `import` statement must appear at the top level of your code before the module is used. That means you cannot import the module inside a function as shown below. In general, it is recommended that you place all `import` statements on the first lines of your code, before any variable declarations and function definitions. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/04/839f931b-dbf0-41e8-909b-ef22b7825e36.png) * In certain cases, the error indication does not appear at the point of your actual mistake, but occurs on the first line on which your mistake causes an error. For example, if you leave out the closing curly brace (`}`) in one of the functions of your page code, an error will most probably occur on whatever the next line of code happens to be. In the code shown below, a closing curly brace is missing from line 6, but the error does not occur until line 8. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/d2a89804-69ff-47f2-b9d2-a8e6a4be5435.png) ## Databases The Databases section of the sidebar contains your database collections. These collections store your site's content and are managed in the [Content Management System (CMS)](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields). + Add a new collection by clicking the plus ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/99d515cc-eb30-435b-adda-0b105109573f.png) icon that appears when you hover over **My Collections**, and then clicking **Create collection**. + Manage an existing collection by clicking the More Actions ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/513de89c-c37e-41e8-8ee2-9b9be5664e96/9f39252c-93d5-4b73-9575-8a49166254f2.png) icon that appears when you hover over a collection name. You can update a [collection's permissions and privacy](https://support.wix.com/en/article/how-do-i-work-with-data-permissions), add a new [dynamic page](https://support.wix.com/en/article/cms-about-dynamic-pages) based on a collection, [remove a collection](https://support.wix.com/en/article/cms-formerly-content-manager-deleting-a-collection), or add and remove [data hooks](https://support.wix.com/en/article/velo-using-data-hooks) to a collection. + Connect external databases in [**External Databases**](https://support.wix.com/en/article/velo-integrating-external-databases-with-your-wix-site). You can manage their content here like any other collection. + When certain Wix Apps are added to your site, database collections are automatically added to your site and appear under [**Wix App Collections**](https://support.wix.com/en/article/cms-formerly-content-manager-working-with-wix-app-collections). Collections for each app are grouped together and are read-only. You can edit the collections in the relevant app manager in your Dashboard. >**Note:** > You may need to save or publish the site and refresh your browser to view the Apps collections in the Database. ## Packages & Apps This section allows you to add complex functionality to your site with packages and apps. ### Packages You can use a code library that suits your needs and incorporate it into your own code. Each reusable library of code is known as a package. There are 2 types of [pre-built packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) that you can install in the **Packages** section of the sidebar - [npm Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) and [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md). + Install a package by clicking the plus ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/03/07/30545cd2-342e-4223-9445-e7760e65566d/b2605497-443f-4089-a367-71c037b85423.png) icon that appears when you hover over **npm** or **Velo Packages**, or click **Install packages from npm** and **Install Velo Package**. + Uninstall a package by hovering over the package you want to uninstall, click the **More Actions** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/347cc5c6-bce3-4407-82c3-e211e02cab98/398ced84-9ef5-4f70-87cd-4cb39a66dd65.png) icon, and select **Uninstall**. ### Apps You can create a custom app with Wix Blocks. Click the plus ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/03/07/30545cd2-342e-4223-9445-e7760e65566d/b2605497-443f-4089-a367-71c037b85423.png) icon, and then **Create New App** to get started. ### Custom Extensions Custom extensions, also known as service plugins, allow you to add your own custom logic to out-of-the-box apps and to integrate services from 3rd party providers. Learn more about [custom app extensions using service plugins](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis). ## GitHub Integration In the GitHub section of the sidebar, you can connect your site to GitHub to write, test, and publish code locally on your computer for your Wix site. To get started, select **Connect to GitHub**. Learn more about [GitHub integration](https://support.wix.com/en/article/velo-about-git-integration-wix-cli). ## Developer Tools Your code will run in your published site or test site, but you may want to [test your code](https://support.wix.com/en/article/testing-and-debugging-code-with-developer-tools#testing) before you publish to make sure it works as expected. The Developer Tools section of the Code sidebar contains tools for: + [Monitoring your code's performance](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/monitoring-your-published-site/working-with-the-monitoring-dashboard.md). + [Viewing logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md#debugging-with-wix-logs). + [Connecting your site to Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md). + [Securing your code's secrets](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md). + [Sending a triggered email](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md). You can test your code before you publish by previewing your site or creating a test site. The code runs the same in Preview Mode and Test Site Mode as it does in the published version. You may also want to [debug your code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) before publishing to identify any potential issues. ## Saving Versions of Your Code When you save your site or your site is [autosaved](https://support.wix.com/en/article/about-autosave), the corresponding code is saved with that [version](https://support.wix.com/en/article/site-versions-with-developer-tools) of your site. If you go to the Site History and revert to a saved version of your site, the code that was saved with that version is restored as well. ## Help The Help section offers links to additional resources: + Check out the [API Reference](https://www.wix.com/velo/reference/api-overview) to explore how to use APIs with your site and to learn basic concepts. + Find examples of the functionality you need with [video tutorials](https://www.wix.com/learn/library/coding) and [code examples](https://dev.wix.com/docs/coding-examples.md). + Connect with Wix developers in [Discord](https://discord.gg/GhdmDN926z) or the [Velo Forum](https://community.wix.com/velo/forum). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Editor: Working with the Code Sidebar ## Article: Working with the Code Sidebar ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md ## Article Content: # Wix Editor: Working with the Code Sidebar The code sidebar shows all of the files that make up your site, including pages, popups, folders, files, packages, and database collections. Working in this sidebar, you can perform a variety of actions that affect your site as detailed below. To begin coding, select **Dev Mode** at the top of the editor and click **Turn on Dev Mode**. The sidebar displays different content depending on which sidebar button you select.
To hide the sidebar, hover over it and click the arrow ![arrow](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3a756c6adf50fb10462dccf4fd752fea.png) icon on the right side of the sidebar. To show the sidebar after it's hidden, click any of the sidebar buttons. ## Page Code The Page Code section of the sidebar contains the following sections: ### Main Pages Your site's regular pages appear immediately beneath the Main Pages section title. You can change a page's [settings](https://support.wix.com/en/article/page-settings-for-dynamic-pages) by clicking the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/171af05b-a957-46f2-a72c-2c3d2c30d939.png) Settings icon that appears when you hover over the page's name. You can set any page other than your site's home page as a [dynamic page](https://support.wix.com/en/article/cms-about-dynamic-pages).  ### Dynamic Pages If you've created any dynamic pages, all the pages with the same [prefix are grouped together in the same section](https://support.wix.com/en/article/about-url-prefixes-and-page-grouping). You can add a new dynamic page to a group by clicking the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/171af05b-a957-46f2-a72c-2c3d2c30d939.png) Settings icon that appears when you hover over a section's name. You can change a page's settings, such as its URL and SEO data, remove its dynamic connections to make it a regular page, or delete it by clicking the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/171af05b-a957-46f2-a72c-2c3d2c30d939.png) Settings icon  that appears when you hover over the dynamic page's name. ### Router Pages If you've created a [router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md), all the pages associated with that router's prefix will be grouped together under the Page Code's Router Pages section of the code sidebar. For example, if you created a router with the prefix **myrouter**, the router pages are grouped together under **Myrouter Pages (Router)**. Each individual router page is given a default name that is used in your router's code. You can change the name, if you want. Your visitors cannot see the page name. You can change a router's prefix (which also changes the names of the associated functions that are implemented in the routers.js file), and/or you can add a new page to a router.  Do this by clicking the   ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2020/12/07/4ca599f4-e5a7-427b-a822-1e1d08db32cc/4161acc3-53b6-4e42-9bcf-001f17e6c5ef.png)   ellipsis icon that appears when you hover over the title of the grouped router pages. Choose either **Change Router** or **Add Page to Router**. You can change a page's settings, rename it, delete it, or remove it from a router to make it a regular page by clicking the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2020/12/07/4ca599f4-e5a7-427b-a822-1e1d08db32cc/4161acc3-53b6-4e42-9bcf-001f17e6c5ef.png)   ellipsis icon that appears when you hover over the name of the router page. You can add a new router by clicking the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/58e95893-4a6b-4bec-b4b8-def3e7299bd0.png) plus icon  that appears when you hover over the Main Pages section header in the code sidebar.  ### App-specific Pages Some apps, like those that add Members functionality to your site add special pages to your site. These pages appear in the their own section under Page Code. ### Popups If you've added a popup to a page on your site, it appears in the **Popups** section of the sidebar. This section only appears if you've added at least one popup to your site. You can add a new popup using the **Add** menu in the Editor.  When you select an existing popup in the sidebar, the Editor enters Popup Mode.  ### Global (Site) Elements in the Editor can appear either on a specific page or on all of your site's pages. You put code for specific pages in that page's code file. Code that you want to run on all the pages in your site should be in the **masterPage.js** file. You could have code that you want to run on all the pages of your site for these reasons: * Code for elements set to appear on all pages: When an element appears on all your site pages and you want to add functionality to it that will be consistent across your site, add that code in the Site tab. When you use the Properties panel to add an event to your element that appears on all pages, the code for that event is automatically placed in the **masterPage.js** file.  * Code in your site that isn't related to a particular element: If you have code that relates to all the pages of your site but doesn't relate to an element set to show on all pages, add it to **masterPage.js**. If you have an element that appears on all pages, but you want to add code to it that is specific to one page, add the code to the Page code for that page.
**Tip:** Code that you add to the **masterPage.js** runs on every page in your site. This can impact your site's performance so it's important to consider if you need the code to run on every page. If you have code that needs to run on some, but not all pages you may want to put it in a [Public](https://support.wix.com/en/article/working-with-the-site-structure-sidebar-9621920#public) file and [import](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/java-script-support.md) it as needed.
## Code Files The Code Files section contains the following sections: ### Public You may have a function that you need to use in multiple pages and you don't want to repeat it on each page. You can write that function once in a **public** file and then call it from any page, or from any backend or public file as needed. The Public section is where you put JavaScript files that contain the code you want to use on any page in your site.  You need to export functions from public files and then import them in the files where you want to use them, public or backend. When you add a new .js file to the Public section it contains comments that provide instructions and examples for exporting and importing functions. You can refer to those comments for more information.  As your site's functionality grows, it may be easier to keep your code organized in files that you call as you need them in your page code. You can divide your public files into folders to further organize them.  * You add a new file or folder to the Public section by clicking the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/cfa4c729-c338-432a-b7dc-cd925f225323/e1105ae1-9594-4c69-9b00-e8b3738add35.png)  that appears when you hover over the section name. * You add a new file to a folder or delete a folder by clicking the ellipsis icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/67cc03b0-219a-4d89-aff8-cee197e647b3/d078bb2b-9a2a-4cba-9b7e-9b01fd2ea345.png)  that appears when you hover over the folder name. * You rename or delete a file by clicking the ellipsis icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/68b0b468-4988-4509-a8f2-67710a777ca4/0807a3fb-993c-4913-b7cd-1748e71ede91.png)  that appears when you hover over the file name.
**Note:** Your page and site code, which are also publicly accessible, do not appear in the Public section.
### Backend The Backend section of the sidebar lists files that are not publicly accessible from your site. Since the backend code isn't visible, you can place sensitive information in the Backend that might otherwise be a [security risk](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md). You can create JavaScript files, [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md), and other files for use in the backend, and you can organize these files in folders. There are several special JavaScript files that may be present in your site's Backend section. For example, the **data.js** file contains the code for [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md), and the **routers.js** file contains the code for [routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) and [data binding router hooks](https://support.wix.com/en/article/about-data-binding-router-hooks). These files are added automatically when you work with these features.  You can also add a Jobs Scheduler file to [schedule recurring jobs](https://support.wix.com/en/article/velo-scheduling-recurring-jobs) and an **events.js** file to [handle backend events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md). * You add a new file or folder to the Backend section by clicking the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/7510d834-919a-45f7-8e3d-0d895156740c/be3b2743-9022-4ab3-848c-6f0ea1162544.png)  that appears when you hover over the section name. * You add a new file to a folder or delete a folder by clicking the ellipses icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/215d30e9-c49c-4c4c-a8b0-4fc331b0e957/860b3bd5-089e-49eb-885a-1ef03e54af26.png)  that appears when you hover over the folder name. * You rename or delete a file by clicking the ellipses icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/1a8f6725-8cf4-4045-9a01-51436fbe3468/c01c827a-8ccd-4636-9ea1-570dee9a5e92.png)  that appears when you hover over the file name. ### Packages Sometimes you need to add complex functionality to your site that has already been implemented by someone else. You can use a code library that suits your needs and incorporate it into your own code. Each reusable library of code is known as a package. There are 2 types of pre-built packages that you can install from the **Packages** section of the sidebar: * [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) * [npm Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md) Velo packages can be divided into 2 categories: * [Built by Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) * [Built by you](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-custom-apps.md) You can install a package by clicking the plus ![](https://lh6.googleusercontent.com/4jdhs_VfyBiaFRS8MqHTATUcv6TsMjPnvUP2PoehCl8QutDrLGpBiPsCP777h1DsjmN84ivcBRVXIeu2GsIYP0a5j7U1ov9PvcRNRZb2Y1378SG7Tta4bO89qcOS0dwd1Gj0TPJF=s0) icon that appears when you hover over the **Packages** section. Once you've installed a package you can manage the package on your site in the Package Manager by clicking the settings ![](https://lh3.googleusercontent.com/jr9zRRow0hiaf24SRlpKmbMONtao6MrrmvJEIYZ52gXR4Vb4PHEqxhir2bOPpLtMtDQy0XLI9jCJLatfFcoYszHWsAc5L7PJKQu810r-8YN2WA7KOeVn8_df0t7a4O-na2U0hTrw=s0) icon that appears when you hover over the **Packages** section. You can uninstall a package by hovering over the package you want to uninstall, click the Show More ![](https://lh3.googleusercontent.com/Ar0G8s4uEGoOgP_f-_hyAzgcfZMoNgVj-y7YsQmkmMsaPqYSwQoaFThgmcYee_Kddppgye3TeEB2iNDx97mC0zxERQKG-Jcvh7AWfGgLg0rpe7mjMtwuaSi8aBb-zMeiuUpK3uEk=s0) icon, and select **Uninstall**. ## Search Your Code Click the magnifying glass sidebar button to open Search Your Code and search all the code files on your site. You can search with options to match case, match whole word, or use regular expressions.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2019/11/06/e174d8f3-a380-4f95-92f8-9a612f28f954/65c3fad5-78b6-422d-b6ba-2c2a42d3565e.png)
## Databases The Database section of the sidebar contains your [Content Collections](https://support.wix.com/en/article/about-database-collections). Click a collection to open it in the Content Management System (CMS). You can add a new collection or work with [external collections](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/adding-and-deleting-an-external-database-collection.md) by clicking the plus ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2017/02/05/99d515cc-eb30-435b-adda-0b105109573f.png) icon that appears when you hover over the section name. You can add a new dynamic page based on a collection, update a [collection's permissions](https://support.wix.com/en/article/how-do-i-work-with-data-permissions), [remove a collection](https://support.wix.com/en/article/cms-formerly-content-manager-deleting-a-collection), or add and remove [hooks](https://support.wix.com/en/article/velo-using-data-hooks) to a collection by clicking the ellipsis   ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/513de89c-c37e-41e8-8ee2-9b9be5664e96/9f39252c-93d5-4b73-9575-8a49166254f2.png)  icon that appears when you hover over a collection name. When you add certain Wix Apps to your site, database collections are automatically added to your site. These collections appear in your sidebar. [Wix App collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) contain information about the items in your Wix app. Collections for each app are grouped together in the sidebar and are read-only. You can edit the collections in the relevant App manager in your Dashboard. >**Note** > You may need to save or publish the site and refresh your browser to view the Apps collections in the Database. ## Developer Tools The Developer Tools section contain the following sections: ### Logs The [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) feature allows you to view information about [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md). It records events, such as console logs, HTTP functions, and web module functions as log entries. You can view and track logs in real time or you can connect your Wix Logs to [Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md), an external monitoring tool to perform log analysis and generate visual depictions of site data for you to gain meaningful insights. ### Monitoring When using backend code with your site, you will want to monitor the code's performance to ensure the best experience for your site visitors.  The [Monitoring dashboard](https://support.wix.com/en/article/velo-monitoring) assists you with the following: * Track the performance of your backend code over time. * Detect when your site is not functioning as expected. * Troubleshoot and determine the causes of a site not functioning smoothly. * Ask for additional resources when needed. ### Security The Security section of the Developer Tools tab contains your [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md). Sometimes you may need to add private information to your site's code. For example, you may require an API key for some 3rd-party service you choose to integrate with. You never want to expose your secrets to your page, site, or public code. Instead, you can use the Secrets Manager to safely work with secrets in your code.  ### Automation In the Automation section you can find the [Triggered Emails](https://support.wix.com/en/article/velo-creating-a-triggered-email) feature.  Triggered Emails allow you to send personalized messages to site visitors who have triggered a specific event. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working in the Code Editor ## Article: Working in the Code Panel ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md ## Article Content: # Velo: Working in the Code Editor You write the code for your site in the code editor at the bottom of the page in both Wix Studio and Wix Editor.  >**Tips:** > * You can drag the code editor up from the bottom of the page to open it. > * See our list of [keyboard shortcuts](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/keyboard-shortcuts.md). ## Code Editor Tabs The code editor displays your site's code files in tabs. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/08/12/7a270757-486a-45ce-aab5-48face4f701a/695ffe3f-181a-40bd-8953-7c1678e06b7f.png)
How code files open in tabs depends on the type of file you are opening.
What files can I open in a new tab? * **Page Code files**. Because you can't select more than one page at a time to view in the Editor, you also can't have multiple page code tabs open simultaneously. There is one tab that displays the page code for the currently selected page. Selecting a page in the **Page Code** section opens that page's code in this tab. When you select a different page, this same tab will display the newly selected page's code.  * **masterPage.js**. You can open **masterPage.js** alongside page code files. * **Code Files**. All the files in the **Code Files** section can be opened in their own tab.
How do I open a new tab? When you click a file in the **Code sidebar** it opens in a new tab in the code editor. When you first open a file, you'll notice that its name is italicized in the tab. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/08/12/179c455b-0997-4b26-82de-bd26e5b2a3fb/b571be6a-aaa0-4d8e-a5ec-87769553aab5.png)
The italics means that the file has not yet been modified. That also means that if you click another file in the **Code sidebar** it will open in the same tab, replacing the file you first clicked. The file name will change to being un-italicized when you either: * Modify the file * Double click the filename in the tab or the Code sidebar Once the filename isn't italicized, if you click another file in the Sidebar, it will open in a new tab.
## Code Editor Toolbar The [Code Editor toolbar](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-with-the-code-editor-toolbar-and-right-click-menu-with-wix-editor.md) is displayed at the top right corner of the code editor. You can click the icons to maximize and minimize the code editor, show or hide the [Properties and Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md), test your code in Preview mode, and display more options. The additional options include links to help content and a list of [keyboard shortcuts](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/keyboard-shortcuts.md), plus a button for switching the Velo code editor theme between light (default) and dark. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/a551efd3-24d0-48fe-8aec-00f810ffc5fc/2022/02/09/7441878a-7bda-4b75-a47b-14e56751ca3e/01a956b0-6c70-4561-934e-24040c1708db.png)
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/a551efd3-24d0-48fe-8aec-00f810ffc5fc/2022/02/09/3adc712c-04ce-4dba-9543-3dda477b7ce2/541a1986-9de0-48be-b5a5-2e9851a61a26.png)
## Right-Click Menu In addition to the buttons in the code editor toolbar, you can right-click anywhere in the code editor to bring up the [right-click menu](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-with-the-code-editor-toolbar-and-right-click-menu-with-wix-editor.md) and display more options.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/3eeeab8e-2074-45f7-ae46-10d01af1b8e4/bf4a8fdb-e2a3-4449-a7c4-ae873728084c.png)
## Velo Syntax and Autocomplete #### Selecting a specific element Velo lets you code using standard JavaScript. It also has a specific syntax, or set of rules, for selecting an element on your page, which is: ```javascript $w('#elementID') ``` If you know jQuery, this should look familiar. If you don't, here's what you need to know. **To select an element:** 1.  Type $w. 2.  Enclose the ID of the element in parentheses and quotes.  3. Add a hashtag before the element ID. >**Note:** > You can use either single quotes or double quotes. To make things even easier, Velo includes code completion. When you type the $, a pop-up window opens listing the elements on your page and the relevant Wix APIs.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/030571df-9516-496b-bc2a-2a8af88f13dd/2022/04/05/c215802a-23ce-41cb-8dc8-25168dcb9afd/5dcf71e1-bc78-4c48-a192-6a371302a21a.png)
Use the up and down arrow keys to select the element you want, and then press Enter. Alternatively, you can click the element in the list. The reference to the element is added to your code with all the necessary syntax. 
**Tips:** * You can press Ctrl+space at any time to view the code completion pop-up. * Element IDs are case sensitive. '#Button1' is not the same as '#button1'. * If you add a new .js file in Public, code completion lists the elements from the last page you were on.
You can find the ID of any element by hovering over it or selecting it. You can change the ID of any element in the Properties & Events panel. #### Selecting multiple elements If you want to select multiple elements by ID, use the same syntax as above to refer to your elements and separate each element with a comma, like this: ```javascript $w('#elementID1, #elementID2, #elementID3') ``` #### Selecting all elements of a specific type To select all the elements of a specific type, use the ID of the element type without the hashtag, like this: ```javascript $w('Button') ``` The ID of the element type is the name of the element as it appears in the [Velo API](https://www.wix.com/velo/reference/$w.html).  #### JavaScript Templates In addition to autocomplete that relates directly to Velo, the code editor also includes autocomplete for standard JavaScript templates and keywords. For example, if you type the word "for," the autocomplete list includes templates for "for statements" as well as the keyword "for." Each template includes a link to a standard JavaScript API where you can read more information.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/030571df-9516-496b-bc2a-2a8af88f13dd/2022/04/05/cde8b78b-cf82-4233-aa42-754e57c5476d/e0e19da3-f80e-4e66-b36c-20d6e95925d7.png)
When you select a JavaScript template, the full syntax for the template is added to the code editor. For example, if you select the "for statement," the following template gets added to your code: for (let index = 0; index < array.length; index++) { const element = array[index]; } All you need to do is to add the code you want to run in the loop. ## Making Sure the Element Has Loaded Before You Reference It When a page loads in a browser, it's possible for the code on the page to run before the page finishes loading. This can cause an error if your code tries to reference an element in the page before it's loaded. Because of this, you need to make sure that all the elements on your page have loaded before you try to access them using code. You do this is by including all your code that uses the `$w` selector in the following function: ```javascript $w.onReady(function() { //TODO: write your page related code here... }); ``` This is only required if you add code on your own using the `$w` selector. Any code you add to a function using the Properties & Events panel runs only after the page loads. Learn more about `$w.onReady` [here](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md). ## Working with Your Elements All of the elements in the Editor have **properties**, **methods**, and **event handlers** that you can use to work with your elements and add functionality to your site. After you select an element, type a period to see the full list of these items. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/a47ea23b-7b34-44ed-a546-9550dc02783b.png)
Use the up and down arrow keys to select the item you want, and then press Enter. The necessary syntax is added to the end of your element selector. As you move through the options, a brief description of the functionality is displayed. Click the "Read more" link for more information.  The autocomplete pop-up also includes standard Javascript methods that you can call on your element. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/dcb32606-79b9-46e9-9d92-d5172d455c86.png)
### Properties Properties contain information about your elements. Some of these are read-only, while others have values you can also set. For example, the text element has an `isVisible` property that returns whether the element is actually visible on-screen. This property is read-only. The text element also has the `text` property that contains the current text in the text element. This is a property you can both read and set. ### Methods Methods perform actions on your elements. For example, the button element has a `hide` method that causes the button not to appear on your site. Some methods have additional options that affect how the action occurs. For example, you can add animation to the `hide` method by specifying it in the parenthesis, like this: $w("#button1").hide("FlyOut"); Here also you'll need to look at the [Velo API](https://www.wix.com/velo/reference/$w.html) to learn all the options. ### Event Handlers [Event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) let your elements respond to user actions (events). When you add an event handler to an element, you also need to specify what you want to happen when the event occurs. You do this in the **callback function** for your event. For example, let's say you have a button that says "Take the Tour" on it. You want to add functionality so that when a visitor hovers over the button the text changes to "Let's Go!". You would add code to your site that looks like this (we've added comments to explain each part of the code): ```javascript $w("#button1").onMouseIn(()=> //onMouseIn is the event handler. // The callback function starts here. { $w("#button1").label = "Let's Go!"; //This is the code that runs when the event occurs. } // The callback function ends here. ); ``` Don't forget that you can also add event handlers to your elements using the [Properties & Events panel](https://support.wix.com/en/article/using-the-properties-panel). Unless you have a specific reason for wanting to add event handlers manually, we recommend using the Properties & Events Panel. >**Note:** > Event code that you add to your site using the Properties and Events panel will not work if you copy/paste it to any other page or site, even if you copy the associated element. ## Warnings and Errors As you write your code in the code editor, you may see warning and error indications. Warnings are indicated in yellow, and errors in red. The indications take the form of a colored wavy line underneath the relevant code and an icon to the left of the line number.  To view the warning or error message, hover over the icon.  ### Warnings A warning in your code is an informational message that brings your attention to some code that you might want to change. Warnings do not stop your code from running and can often be safely ignored. Warnings are indicated by a yellow triangle and yellow wavy underline.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/1bbf5ef0-70b3-484b-9251-db04266b3ac7.png)
A common warning message occurs when you have an unnecessary 'else' after 'return'. This most often occurs when you use the following coding pattern: function myFunction() { if(someCondition) { //do some stuff return 0; } else { //do other stuff return 1; } } If `someCondition` is true, the function will return. That means that we don't need the `else` to stop the code from executing when `someCondition` is `true`. You can safely ignore this warning or change your code to the following pattern: function myFunction() { if(someCondition) { //do some stuff return 0; } //do other stuff return 1; } ### Errors An error in your code means that your code will not function properly. Depending on the type of error, your code either will not work as expected or might not run at all. Make sure to fix all errors in your code before publishing your site for your site visitors to use.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/dcfaf64d-0e63-4467-90ab-d2163802ab0c.png)
Here are some common situations where you might find errors in your code. * Error message: "#text1" is not a valid selector (see image above) If you change the ID of an element that you are using in some of your page code, the `$w()` selections in your code will cause errors. For example, if you have a text element with the ID **text1** and you change the ID to **statusMessage**, all instances where you selected the element using `$w('#text1')` will now be errors. Note: you can use the Search and Replace functionality to fix this error throughout your code. * Error message: 'import' and 'export' may only appear at the top level When you import an API module, the `import` statement must appear at the top level of your code before the module is used. That means you cannot import the module inside a function as shown below. In general, it is recommended that you place all `import` statements on the first lines of your code, before any variable declarations and function definitions.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/04/839f931b-dbf0-41e8-909b-ef22b7825e36.png)
* In certain cases, the error indication does not appear at the point of your actual mistake, but occurs on the first line on which your mistake causes an error. For example, if you leave out the closing curly brace (`}`) in one of the functions of your page code, an error will most probably occur on whatever the next line of code happens to be. In the code shown below, a closing curly brace is missing from line 6, but the error does not occur until line 8.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/d2a89804-69ff-47f2-b9d2-a8e6a4be5435.png)
## Media Manager Integration Velo allows you to use images that you've stored in the Wix Media Manager in your code. When you work with elements that include an image property, such as `src`, a pop-up window opens, giving you the option to use an image from the Media Manager. This window appears automatically as you type "src".
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/15/8b4f1347-fb9d-477f-9c31-30de0bca509a.png)
## Testing Your Code Your code will run on your published site or test site. However, you may want to [test your code](https://support.wix.com/en/article/testing-and-debugging-code-with-developer-tools#testing) before publishing it to ensure it works as expected. You can test your code before you publish by previewing your site or creating a test site. The code runs the same in Preview Mode and Test Site Mode as it does in the published version. You may also want to [debug your code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) before publishing to identify any potential issues. ## Saving Versions of Your Code When you save your site or your site is [autosaved](https://support.wix.com/en/article/about-autosave), the corresponding code is saved with that [version](https://support.wix.com/en/article/site-versions-with-developer-tools) of your site. If you go to the Site History and revert to a saved version of your site, the code that was saved with that version is restored as well. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Keyboard Shortcuts ## Article: Keyboard Shortcuts ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/keyboard-shortcuts.md ## Article Content: # Velo: Keyboard Shortcuts |Action |Mac |Windows | |---|---|---| |Format code|⌥⇧F|Alt + Shift + F |Search in file|⌘F|Ctrl + F |Search in all files|⇧⌘F|Ctrl + Shift + F |Undo|⌘Z|Ctrl + Z |Redo|⇧⌘Z|Ctrl+ Shift + Z |Comment in/out|⌘/|Windows + / |Add cursor|⌥ click|Alt + click |Add cursor above|⌥⌘↑|Alt + Ctrl + ↑ |Add cursor below|⌥⌘↓|Alt + Ctrl + ↓ |Move line up|⌥↑|Alt+↑ |Move line down|⌥↓|Alt+↓ |Duplicate line down|⌥⇧↓|Alt + Shift + ↓ |Duplicate line up|⌥⇧↑|Alt + Shift + ↑ |Create/extend a vertical non-contiguous selection|⌥⇧ click|Alt + Shift + click |Toggle fold (Region at cursor)|⌘KL|Ctrl + K + L |Fold all regions|⌘K0|Ctrl + K + 0 |Unfolds all regions|⌘KJ|Ctrl + K + J |Fold level X|⌘K2  (for level 2)|Ctrl + K + 2 * * * ### File Search Shortcuts |**Action** |Mac |Windows | |---|---|---| |Find next|⌘G|F3 |Find previous|⇧⌘G|Shift + F3 |Replace|⇧⌘1|Ctrl + Shift + 1 |Replace all|⌥⌘⏎|Ctrl + Alt + Enter |Match case|⌥⌘C|Alt + C |Match whole word|⌥⌘W|Alt + W |Use regular expression|⌥⌘R|Alt + R --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using JSDoc ## Article: Using JSDoc ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/using-jsdoc.md ## Article Content: # About Type Checking and Autocomplete Using JSDoc Tags [JSDoc](https://jsdoc.app/) is a popular markup language used to document JavaScript code, applying type checking and autocomplete as you write your code. You can use JSDoc in a Wix environment in the same way as you would in any JavaScript environment. Adding JSDoc to your custom code allows you to benefit from [type checking and code autocomplete](#type-checking-and-autocomplete). ## Supported IDEs You can add and benefit from JSDoc using: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). You can [import JSDoc](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/import-jsdoc.md) only in the editor. ## JSDoc tags and definitions JSDoc tags are added to JavaScript files as comments. Each line of JSDoc defines an item using the following format: ```js /** * @ {} */ ```
JSDoc tags | Variable | Description | | -------- | ----------- | | `jsdoc-tag` | [JSDoc tag](https://jsdoc.app/) that defines what the item is.
For example, use `@typedef` to define a new type, `@property` to define the property of an object, and `@param` to define a parameter. | | `type` | Data type of the item. For example, `object`. | | `item-name` | How the item is referred to. | | `description` | Description of the item.
Optional. |
### Where to add JSDoc in your code You can add JSDoc to Velo code as you would to any JavaScript code, by including the annotations just above the code you are documenting. However, when defining a parameter type, using the `@param` tag, place the JSDoc definition directly above the function declaration. #### JSDoc example In the following example, we define an `employee` type that is then applied to a function parameter. ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {number} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. * * @param {employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${employee.hours * employee.seniority * 100}`; } ``` ## Type checking and autocomplete When you apply JSDoc types to parameters, the editor can perform type checking and provide autocomplete suggestions for function arguments. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/using-js-doc-md_velo-articles_velo-workspace_images_typecheck-jsdoc.png) ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/using-js-doc-md_velo-articles_velo-workspace_images_autocomplete-jsdoc.png) ## File types You can add JSDoc in any JavaScript file, meaning the file's suffix is `.js`. This includes JavaScript files in Velo packages, allowing anyone using your packages to use type checking and autocomplete. You can import types defined by Wix in some specific file types. For example, you can import backend event objects to [`events.js` files](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md).
Important: Web.md modules (files with the suffix `.jsw`) support autocomplete, but not type checking.
## See also - [Import JSDoc](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/import-jsdoc.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Import JSDoc ## Article: Import JSDoc ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/import-jsdoc.md ## Article Content: # Set up Type Checking and Autocomplete by Importing JSDoc Types You can import JSDoc types to use in your JavaScript code. Apply a JSDoc type to a parameter to enable [type checking and autocomplete](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/using-jsdoc.md#type-checking-and-autocomplete).
Important: Importing JSDoc is supported only in the code editor, not in the Wix IDE or your local IDE.
## Step 1 | Define and export a type You can skip this step if you want to use a type defined by Wix. To define and export a type using JSDoc: 1. Use [JSDoc tags and definitions](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/using-jsdoc.md#jsdoc-tags-and-definitions) to define a type. For example: ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {string} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. */ ``` 1. You can expose your type to your site's files as either a global type or a restricted type: - **Global type**: You can use your defined type in any JavaScript file without further changes. - **Restricted type**: You can define different types with the same name in different files. To restrict your type, add `export {};` below your JSDoc declaration. For example: ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {string} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. */ export {}; ``` ## Step 2 | Access a JSDoc type The way that you import the JSDoc type depends on where and how the type is exposed: - [Self-defined global type](#self-defined-global-type) - [Self-defined restricted type](#self-defined-restricted-type) - [Wix editor elements](#wix-editor-elements) - [Backend event handlers](#backend-event-handlers) - [Router functions](#router-functions) ### Self-defined global type Use the type without importing it. For example: ```js /** * @param {employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${employee.hours * employee.seniority * 100}`; } ``` ### Self-defined restricted type Import your type using `import('').` directly in the JSDoc's tag type declaration. For example: ```js /** * @param {import('backend/myJSDocFile.js).employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${employee.hours * employee.seniority * 100}`; } ``` ### Wix editor elements You can use [Wix editor elements](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) directly in JSDoc types without importing them. Define the JSDoc data type using `$w.Element`. For example: ```js /** @param {$w.Text} parameterName */ ``` ### Backend event handlers By default, the file for [backend event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md), `events.js`, doesn't know the type of the parameter you are passing to a function. To tell your `events.js` file the parameter's type: 1. Check your event's `EventObjectName`. You can find it in the method declaration in [API reference](https://dev.wix.com/docs/velo.md). 1. Add the following code above your function declaration: ```js /** @param {import('api-module-name').Events.} parameterName */ ```
Example: onBookingCreated.md To add JSDoc to wix-bookings.v2 event, onBookingCreated():
  1. Open the API reference for onBookingCreated..md Find the EventObjectName.
  2. Add the following JSDoc annotation above the event handler function in the events.js file:
    /** @param {import('api-module-name').Events.BookingCreated} event */

/** @param {import('api-module-name').Events.BookingCreated} event */
export function wixBookings_onBookingCreated(event) {
  // Handle your event
}
### Router functions By default, the [routers.js](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) file doesn’t support autocomplete or type checking for [WixRouterRequest](https://dev.wix.com/docs/velo/api-reference/wix-router/wix-router-request/introduction.md) objects passed to router functions. To support autocomplete or type-checking in a `routers.js` file, add the following JSDoc annotation above your router functions: ```js /** @param {import('wix-router').WixRouterRequest} parameterName */ ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with the Code Editor Toolbar and Right-Click Menu (with Wix Editor) ## Article: Working with the Code Panel Toolbar and Right-Click Menu (with Wix Editor) ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-with-the-code-editor-toolbar-and-right-click-menu-with-wix-editor.md ## Article Content: # Velo: Working with the Code Editor Toolbar and Right-Click Menu (with Wix Editor) > __Note__: This article is about the code editor in Wix Editor. To learn about the code editor in Wix Studio, see [Wix Studio: Working with the code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md#code-editor). The code editor contains tools to help you work with your code. These are split between the toolbar and the right-click menu. ## Code Editor Toolbar |Item |Description | |---|---| |**Preview**
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/a551efd3-24d0-48fe-8aec-00f810ffc5fc/2022/02/09/9f4e8b61-b112-4eb9-bec1-62830f1baa5b/6a923283-4c9c-4699-a09a-3da79306e460.png)|Runs your code in Preview mode. |**Properties & Events**|Shows and hides the [Properties & Events](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) editor. |**Maximize**
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2016/07/21/e538c20e-7b2d-4525-bf8c-94ed69cc18be.png)|Maximizes the code editor on the screen. |**Restore**
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2017/06/04/4aecd178-0ab1-4573-a425-db7d34482f3a.jpg)|If the code editor has been maximized, restores it to its previous height. |**Minimize**
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2016/07/21/273d9743-1a02-4102-a849-bbec82dd80c2.png)|Minimizes the code editor to its collapsed state. |**More**|Links to help content and a list of keyboard shortcuts. Also contains a radio button for switching the code editor theme between light (default) and dark. ## Code Editor Right-Click Options |Item |Description | |---|---| |**Format**|Makes your code neater and easier to read by fixing its indentation, removing extra lines, and applying other standard formatting styles.| |**Find**
[](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2016/07/21/3a849248-6e4a-4ff4-8b2b-a50389ccddb9.png)|Opens the Find editor, which contains the following options:| ||**Find** box. Specify the term you are searching for.| ||**Match Case**. Select this button if you want your search to be case sensitive.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/08/06/bf8f2e02-e880-4d4e-ac48-e74a24f31104.png)| ||**Match Whole Word**. Select this button if you want to limit your search to whole words only.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/08/06/73564097-c8d0-4069-b9bc-db993db86fd1.png)| ||**Regular Expression**. Select this button if you want to use a regular expression as your search term.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/08/06/a3fb7bb2-f6cc-444d-b8c8-e06a9ce15dc3.png)| ||**Replace** box. Specify the term you want to use to replace the search term.| ||**Previous.** Jumps to the previous instance of the search term.| ||**Next.** Jumps to the next instance of the search term.| ||**Replace**. Replaces the current instance of the search term with the replace term.| ||**All**. Replaces all instances of the search term with the replace term.| |**Undo**|Undoes your previous action in the code editor. The Undo action in the code editor is independent of the Undo action in the main Editor.| |**Redo**|Re-does the last action you undid in the code editor. The Redo action in the code editor is independent of the Redo action in the main Editor.| --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Colors in the Code Editor ## Article: About the Colors in the Code Panel ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/about-the-colors-in-the-code-editor.md ## Article Content: # Velo: About the Colors in the Code Editor Velo applies syntax highlighting to the code that you write in the code editor. That means certain parts of your code appear in different colors and font weights depending on their purpose. Syntax highlighting makes it easier for you read and write your code. In this article, we describe the highlighting of some of the most common syntactic elements. We use the following form and page code that calculates the area of a circle based on a given radius.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/01/28/90e858be-35e1-4ce9-baf6-273104fbf362.png)
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/eb89d19e-25c2-42d4-ad45-59ab9e5a56a1.png)
### Keywords Keywords are words that have a special meaning in a programming language. For example, in JavaScript, `let`, `function`, and `return` are all keywords.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/c5115a0e-1e39-4383-a463-1707d74ebbe0.png)
### Function Names in Function Declarations Function declarations include a function name. The function name is used when calling the function in other parts of your code. For example, the `calculateArea` function defined on line 11 in the example below is called on line 5.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/d2cf79c4-a3b6-450b-a26d-0619158b2cd9.png)
### Parameter Names in Function Declarations Function declarations may include one or more function parameters. The function parameters serve as the names of the arguments passed to the function. For example, the `calculateArea` function defined on line 12 in the example below contains one parameter named `radius`. That parameter name is used on line 13 when calculating the area. When the function is called on line 5, the `value` of the `radiusInput` is the argument passed to the `radius` parameter of the `calculateArea` function.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/b903d06d-b01a-4a99-a0fe-57a7b3fcdd4d.png)
### String Literals String literals come in three different varieties in JavaScript: * Double quoted strings `"literal value"` * Single quoted strings `'literal value'` * Template strings `` `literal value ${nonliteral}` `` In Velo, you will often use string literals to select elements using the `$w()` function as shown on line 4 in the example below. You might also use literal values to set the value of an element's property as shown on line 6, set the value of a variable, or pass them as arguments to functions.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/ba476093-ae50-474b-826f-0583b2b6feed.png)
### Number Literals Number literals are used to set the value of an element's property, set the value of a variable as shown on line 1 in the example below, or pass them as arguments to functions as shown on line 13.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/ccf53bb2-76d7-49e8-8540-e799ec10c94c.png)
### Comments Comments are parts of your code that are not executed when the code is run. Usually, you add comments to explain what a particular part of your code is doing.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/12/d42fa595-1168-4f24-8ece-8fdfcac10ff0.png)
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Properties & Events Panel ## Article: Working with the Properties & Events Panel ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md ## Article Content: # About the Properties & Events Panel The Properties & Events panel allows you to add properties and functionality to your page elements. With the panel you can: - Access the [Velo API reference](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) for the selected element. - View and manually update the ID of the selected element. - Set default values for the properties of the selected element. - Add an event handler function declaration to your code to interact with the selected element.
The Properties & Events panel is located to the right of the code editor. **Wix Studio**
## Supported IDEs You can access the Properties & Events panel in the editor (Wix Studio and Wix Editor) **Wix Editor**
The Properties & Events panel changes content when you select an element. The properties and event handlers listed in the panel differ based on the element that you select. You can close and open the panel with the Properties & Events button in the toolbar:
## The ID property All elements come with a default ID value. When you select an element, its ID is displayed in the panel. You can manually change the ID of your element. Assigning meaningful IDs to your elements can make your code easier to maintain.

Important:
If you change the ID of an element, make sure to update any existing code that uses the ID of that element.

## Default state properties Certain boolean properties for your selected element appear under the Default Values section of the panel. They allow you to set an element’s state when the page loads. This limits the need to set an element’s initial state with code, keeping your code cleaner and more concise. Set the element’s initial state by selecting or clearing the checkbox to the left of the property. The available default state properties are: - **Hidden:** Hides the element from your site visitors when your page loads. You can use code to unhide the element upon an action or after a set amount of time. Hidden elements take up space on your page and appear faded in the editor until they are selected. An element's hidden state can also depend on whether its parent element is hidden or collapsed. - **Collapsed:** Collapses the element and hides it from your site visitors when your page loads. You can use code to expand the element upon an action or after a set amount of time. Collapsed elements don’t take up space on your page and appear with diagonal hatching lines in the editor until they are selected. Collapsing and expanding can cause other elements to shift on your page. An element's collapse state can also depend on whether its parent element is hidden or collapsed. - **Enabled:** Enables interactive elements such as [buttons](https://dev.wix.com/docs/velo/api-reference/$w/button/introduction.md) and [text inputs](https://dev.wix.com/docs/velo/api-reference/$w/text-input/introduction.md). Elements with this property are initially in an enabled state. Clear the checkbox to disable the element when the page loads. You can use code to later enable the element when you want visitors to interact with it. Disabled elements appear faded in the editor. >**Notes:** > - The properties displayed in the panel change depending on the selected element. > - To get or set properties with code, see the [Velo API reference](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) for a full list of an element’s properties. ## Event handlers Event handlers appear under the **Event Handlers** section of the Properties & Events panel. Event handlers allow you to add code that defines what happens when site visitors interact with this element. Clicking on an event handler in the panel adds the event handler’s function declaration to your page code for you. ```js $w('#myButton').onClick((event) => { // Add code here }) ``` Like properties, event handlers are specific to the element. Common event handlers that appear in the panel for many elements include: - `onBlur()` - `onClick()` - `onDblClick()` - `onFocus()` - `onMouseIn()` - `onMouseOut()` - `onViewportEnter()` - `onViewportLeave()` Other event handlers can appear in the panel depending on the element that is selected. For example, when a [Gallery element](https://dev.wix.com/docs/velo/api-reference/$w/gallery/introduction.md) is selected, the panel includes additional event handlers to some of the ones listed above, such as: - `onCurrentItemChanged()` - `onItemClicked()` - `onPlay()` - `onPause()` See the [Velo API reference](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) for descriptions of all the event handlers for each element. ## Events AI assistant in Wix Studio The Properties & Events panel in Wix Studio also comes with an AI Assistant button.
Use the AI assistant to help create interactive code for your event handlers. The AI assistant allows you to select an event handler and then describe what the code should do. It generates a code example that you can add to your page code.
It’s important to note that AI can make mistakes and that you should double check that any AI generated code is accurate before using it. >**Note:** > The AI Assistant button is only supported in Wix Studio. ## See also - [About Event Handlers in Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) - [Add an Event Handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) - [Tutorial: Change the Text Label of a Button with Events](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/change-the-text-label-of-a-button-with-events.md) - [Velo API reference](https://dev.wix.com/docs/velo.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Studio: About the Wix IDE ## Article: Wix Studio: About the Wix IDE ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md ## Article Content: # Wix Studio: About the Wix IDE
**Note**: Some features described in this article aren't yet available to all users.
Wix Studio includes a code editor for adding code directly to your site's pages. For a more advanced option, the Wix IDE provides a browser-based environment built on Visual Studio Code. The Wix IDE also features the [Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), an intelligent companion that offers real-time coding assistance. > **Note:** > The Wix IDE is currently unavailable for sites using [Git Integration](https://support.wix.com/en/article/velo-about-git-integration-wix-cli). ## Understanding your site's file structure Wix sites have a specific file structure that Wix uses to run your code. When you open the Wix IDE, you see the file structure for your site.
![IDE file tree](https://github.com/wix-incubator/wix-code-docs/assets/89579857/66325e96-7151-4435-8008-ae3b5cb75d33)
Your site's file structure includes these elements: * The **src** folder, that contains the following folders: * [backend](https://support.wix.com/en/article/wix-studio-about-the-wix-ide#backend-folder) * [pages](https://support.wix.com/en/article/wix-studio-about-the-wix-ide#pages-folder) * [public](https://support.wix.com/en/article/wix-studio-about-the-wix-ide#public-folder) * [styles](https://support.wix.com/en/article/wix-studio-about-the-wix-ide#styles-folder) * The `jsconfig.json` file and `.wix` folder found in the repo's root folder. >**Important:** >The `jsconfig.json` file and `.wix` folder are used to support type checking and autocomplete in the IDE. You don't need to edit these files. Changes to these files aren't synced to your site and are lost when you close the IDE. >**Note:** >Wix doesn't support adding files for web crawlers and bots such as `robots.txt`, `ads.txt`, and `security.txt` to your site. If you add these files, they're ignored by Wix. Here is an explanation of the different subfolders in the `src` folder: ### Backend folder This folder contains the backend code files for your site. Some backend code files are automatically added to this folder, for example, when you add a router to your site, a `router.js` file is added to your backend folder. If you don't see the file that you need, you can create it. The backend folder may contain the following files: + **Web Module files:** These are files that allow you to expose functions in your site's backend that you can run in your frontend code. These files require a `.web.js` file extension. A web module contains one or more [web methods](https://support.wix.com/en/article/about-web-methods). + **data.js**. A file for [adding data hooks](https://support.wix.com/en/article/velo-using-data-hooks) to your site's collections. + **routers.js** A file for implementing [routing and sitemap](https://support.wix.com/en/article/velo-about-routers#routing-code) functionality for your site. + **events.js** A file for implementing your site's [backend event handlers](https://support.wix.com/en/article/velo-backend-events). + **http-functions.js** A file for implementing [HTTP endpoints](https://www.wix.com/velo/reference/wix-http-functions/introduction "_blank") that are exposed on your site. + **jobs.config** A file for [scheduling recurring jobs](https://support.wix.com/en/article/velo-scheduling-recurring-jobs). Jobs consist of backend code that's run at regular intervals. + **General backend files** JavaScript code files. You can import code from these files into any other backend file on your site. These files require a `.js` file extension. Use the following syntax to import code from backend files: ```js import { myFunctionName } from 'backend/myFileName'; ``` Trying to import from the relative path in your site's files doesn't work. #### Config folder If you add a [Velo Package built by Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) to your site, the backend folder contains a folder called `__config__`. This folder contains an editable `config.json` file for defining specific settings for your package. #### Service plugin folder If you add [service plugins](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis) to your site, the backend folder contains a folder called `__spi__`. This folder contains subfolders with the code files for each service plugin. Learn more about [implementing service plugins](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis#implementing-a-custom-extension-with-a-velo-spi).
permissions.json >**Important:** >You only need to use this file if you export backend functions from `.jsw` files. We recommend using web methods and exporting backend functions from `.web.js` files instead. Learn more about [web methods](https://support.wix.com/en/article/about-web-methods). The backend folder also contains the `permissions.json` file. This file defines [permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) for the functions in your web module files. The file contains a key, `"web-methods"` which contains an object. Each key in that object corresponds to a web module file in your backend folder. Name these keys with the following syntax: `"backend/{path to file}/myFile.jsw"`. The value for each file name key is an object that contains keys named after the functions in that file. Those objects contain keys for each permission level. For example: ```json { "web-methods": { "backend/myFileName.jsw": { "myFunction": { "siteOwner" : { "invoke" : // Boolean }, "siteMember" : { "invoke" : // Boolean }, "anonymous" : { "invoke" : // Boolean } } } } } ``` Set the permissions for each function using the following values: * **Owner-only access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `false` * `anonymous.invoke` : `false` * **Site member access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `true` * `anonymous.invoke` : `false` * **Anyone can access**: * `anonymous.invoke`: `true` * `siteMember.invoke` : `true` * `anonymous.invoke`: `true` The `"web-methods"` object must also contain a `"*"` key. The value for this key defines the default permissions that are applied to any function whose permissions you don't set manually. Here is a sample `permissions.json` file for a site with a backend file called `helperFunctions.jsw`. The file's functions are called `calculate`, `fetchData`, and `syncWithServer`. In this case anyone can call `calculate`, site members can call `syncWithServer`, and only site owners can call `fetchData`. ```json { "web-methods": { "*": { "*": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } } }, "backend/helperFunctions.jsw": { "calculate": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } }, "fetchData": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": false }, "anonymous": { "invoke": false } }, "syncWithServer": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": false } } } } } ```
### Pages folder This folder contains code files for each of the pages on your site as well as the [masterpage.js](https://support.wix.com/en/article/velo-working-with-the-velo-sidebar#global-site) file. The code you add to these files runs when visitors open pages on your site. When you add a page to your site in the editor, a code file for that page is added to this folder. The name of the file has 2 components: the name of the page that you define when you create it, and an ID string for internal use. The sections are separated by a period.
![image](https://user-images.githubusercontent.com/89579857/188305074-6e2ee718-13b8-435d-9c75-bcb126f35718.png)
When you [add a dynamic page](https://support.wix.com/en/article/content-manager-about-dynamic-pages#adding-dynamic-pages) to your site, 2 code files are added to this folder corresponding to the dynamic list and dynamic item pages. When you open a page's code file, you see the same sample code that appears in the Wix Studio code editor.
![image](https://user-images.githubusercontent.com/89579857/184646571-1e14f166-2b86-4f21-bf57-83468251bca8.png)
When you delete a page in the editor, the page's corresponding code file is deleted as well. > **Warning:** > Do not rename code files for pages. Wix uses these file names to associate the files with the appropriate pages on your site. If you rename a file, your code is ignored and a new code file is created for the page. ### Public folder This folder contains the public code files for your site. You can import code from these files into any other file on your site. Use the following syntax to import code from public files: ```js import { myFunctionName } from 'public/myFileName'; ``` Trying to import from the relative path in your site's files doesn't work. ### Styles folder This folder contains custom CSS files for your site. Add custom CSS to your site in a file called `global.css`. If you don't see this file, you can create it. The styles defined in this file are applied to all the pages on your site. Many [Wix Editor Elements](https://www.wix.com/velo/reference/$w) have specially defined class names that you can use to style them. You can also create custom class names to use in your CSS code. To learn about supported elements and their class names, see the [Velo API Reference](https://www.wix.com/velo/reference/$w/styling-elements-with-css). You can also see an element's predefined classes and add custom class names to an element in the [**CSS Classes panel**](https://support.wix.com/en/article/coding-with-the-wix-studio-sidebar#css-classes-panel). Learn more about [writing CSS code for your site](https://www.wix.com/velo/reference/$w/styling-elements-with-css). ## Using the Wix IDE To use the Wix IDE, do the following: 1. Click **Code in Wix IDE** in the top right corner of the code editor. ![Page Code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/791db58c3a225b5c0df252113cbf1b77.png) 2. The Wix IDE opens in a new browser tab. You can now edit your site's code. Changes saved in the IDE are automatically synced to your site. The IDE's autosave feature is enabled by default. You can [disable autosave](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) in the IDE's Settings editor. **Note:** To use backend functions in your public and page code files, export functions from your backend files using [web methods](https://support.wix.com/en/article/about-web-methods). 3. Test your code using a [test site](https://support.wix.com/en/article/creating-a-test-site) or by [previewing your site](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site). You can see messages logged from your code in [Wix Logs](https://support.wix.com/en/article/velo-viewing-live-site-monitoring-events#how-it-works) or by [connecting your site](https://support.wix.com/en/article/velo-connecting-site-monitoring-events-to-google-operations-formerly-stackdriver) to Google Cloud Logs (Cloud Logging). 4. When your code is ready, publish your site to make your changes live. ## Editor-only actions You need to do certain parts of the Wix development flow in the editor. These include: + Adding [NPM](https://support.wix.com/en/article/velo-working-with-npm-packages#installing-a-package), [Velo](https://support.wix.com/en/article/velo-working-with-velo-packages-built-by-wix#setting-up-a-package), and [private](https://support.wix.com/en/article/velo-working-with-your-velo-packages) packages. Once you add these packages in the editor, you can import them in your code files in the IDE. + [Adding](https://support.wix.com/en/article/wix-editor-adding-a-page-to-your-site#adding-a-new-page), changing, or deleting pages. + [Changing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) element IDs or properties. + [Previewing](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site) your site or [creating a test site](https://support.wix.com/en/article/creating-a-test-site). + [Publishing](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#publishing-your-site) your site. + Creating a [service plugin](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis#implementing-a-custom-extension-with-a-velo-spi). ## Concurrent editing Two or more site contributors can edit a site's code at the same time in the Wix IDE. Edits made in one instance of the IDE are synced to the other instance in real time. However, you can't edit your site's code in both the Wix IDE and the Wix Studio Code panel at the same time. You also can't edit your code in the Code panel if other site contributors are editing in the Wix IDE. When you open the Wix IDE, the Code panel switches to read-only mode. The Code panel displays this message: ![Go to Wix IDE button](https://github.com/wix-incubator/wix-code-docs/assets/89579857/b0c53a04-962c-4a88-bf0c-18f521961e5e) If you want to edit your site's code in the Code panel, all site contributors must close the Wix IDE. You can then click **Start Coding** in the Code panel. ![Start Coding button](https://github.com/wix-incubator/wix-code-docs/assets/89579857/b5006237-9348-4f84-9597-3cfd82ae952e) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Wix IDE Beta Release ## Article: About the Wix IDE Beta Release ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/about-the-wix-ide-beta-release.md ## Article Content: # About the Wix IDE Beta Release The Wix IDE is currently in beta release. While this release is stable and can be safely used for building and modifying production sites, it still lacks important functionality as listed in the [known issues](#known-issues) section. As we continuously improve the Wix IDE, you can stay informed about new updates in the [release notes](#wix-ide-beta-release-notes) below. As a separate environment from the editor, the Wix IDE allows you to focus on code and instantly see your code changes in Preview mode on a separate tab. Although the tutorial upon opening the Wix IDE is not yet fully tailored to the Wix environment, you can enjoy the IDE features such as refactoring capabilities, go to definition, command palette, split view, improved navigation, and more. Learn more about working with the [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md). You can also check out the README file in your project's root level.

## Known issues Before getting started with the Wix IDE, it's important to note the following known issues that we are working to fix: - **Auto-complete is not fully functional**
[IntelliSense](https://code.visualstudio.com/docs/editor/intellisense), VS Code's language support feature, doesn't currently recognize all NPM packages, Velo packages, Wix Blocks apps, backend modules, and editor elements as expected. You may experience false positive error highlighting for valid code that can run successfully. - **Functional testing is unavailable**
[Functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) isn't currently available in the wix IDE. For functional testing, use the code editor. - **Terminal is not fully supported**
The Wix IDE doesn't require the terminal console to function. While the terminal is available for use, it only offers limited functionality. - **Built-in Git functionality is not supported**
Currently, you can't initialize a local Git repository or publish to GitHub from the Wix IDE. - **Git Integration and Wix IDE can't connect simultaneously**
You can't use both the [Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) and the Wix IDE at the same time. If you connect your site to GitHub with Git Integration, the Wix IDE is unavailable until you disconnect your site from GitHub. The reverse is also true. - **Settings and extensions don't persist between sessions**
In the Wix IDE you can modify settings and install extensions from the [Open VSX Registry](https://open-vsx.org/). However, settings and extensions only apply to the IDE of the site you're currently working in, and aren't retained between Wix IDE sessions for the same site. You will have to reset your settings as well as re-install your extensions at the start of each new session. Note that a session concludes after 30 minutes of inactivity. ## Wix IDE beta release notes Stay up to date on our latest changes and additions as we continue to improve the Wix IDE. Your feedback is important to us. Please help us by reporting any issues that you notice while working with the Wix IDE. | Date | Description | | ------------------- | ------------------------------------------------ | | August 2, 2023 | Wix IDE beta available in Wix Studio. | | March 10, 2024 | Element IDs automatically sync with your IDE when you make changes to your site. ID changes were previously out of sync and required you to sync manually. | | March 26, 2024 | The **Display IDs** toggle is now visible in the **Layers** panel of the editor, while working in the Wix IDE. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/wix-ide-limitations-md_velo-articles_coding-with-wix-studio_images_layers-toggle.png) | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Git Integration & Wix CLI for Sites ## Article: About Git Integration & Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md ## Article Content: # About Git Integration & Wix CLI for Sites Git Integration & Wix CLI for Sites is a set of tools that allows you to write, test, preview, and publish code for your Wix site using your preferred IDE. Your site code is stored in a GitHub repository, giving you version control and making collaboration easy.
## Code in your preferred IDE Git Integration & Wix CLI for Sites allows you to connect your site to GitHub and develop using your preferred IDE. Wix sets up a repository for your project, which you can clone to your computer or cloud workspace and write code in as you would in any other non-Wix project. Read how to [set up Git integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/setting-up-git-integration-wix-cli.md). ## Test your code with the Local Editor The [Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md) allows you to test changes made to your site in real time. The code in your IDE is synced with the Local Editor so you can test your changes before committing them to your repo. You can also change your site's design in the Local Editor and sync it with your IDE. ## Preview and publish with the Wix CLI for Sites The [Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) is a tool that allows you to work with your site locally from the terminal. You can use it to build a preview version of your site and publish it. You can also use the CLI to install [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) to your site. ## Work with multiple collaborators at once Git Integration & Wix CLI for Sites extends Wix Studio's [concurrent editing](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) capabilities. Invite other developers as collaborators on your [site](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) and your [GitHub repo](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/inviting-collaborators-to-a-personal-repository). Multiple developers can work on a site's code at once. ## Version control With Git integration, every change to your site's code is tracked in your GitHub repository, giving you a clear history of edits and the ability to revert to previous versions when needed. This makes it easy to manage updates, resolve conflicts, and maintain a stable development workflow—even when working with multiple collaborators. ## Get started - To get started, read how to [Set Up Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/setting-up-git-integration-wix-cli.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Setting Up Git Integration & Wix CLI for Sites ## Article: Setting Up Git Integration & Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/setting-up-git-integration-wix-cli-for-sites.md ## Article Content: # Set Up Git Integration & Wix CLI for Sites [Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) allows you to write, test, preview, and publish code for your Wix site from your preferred IDE. This article explains how to set up Git Integration & Wix CLI for Sites by connecting your site to GitHub, cloning your site's GitHub repository to your computer, and installing the Wix CLI for Sites. > **Note:** You can also clone the repo to a cloud IDE workspace, then install and use the Wix CLI for Sites there. ### Before you begin Make sure you have the following tools installed before you start: * [Git](https://git-scm.com/download) * [Node](https://nodejs.org/en/download/), version 20.11 or later. * [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [yarn](https://yarnpkg.com/getting-started/install) * An SSH key [added to your GitHub account](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account). ## Step 1 | Connect your site to GitHub To write your site's code locally from your computer, first create a GitHub repo to store your site's code and connect it to your site.
**Important:** * You can't connect your site to GitHub if you have [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) set up on your site. This feature isn't supported when using Git Integration & Wix CLI for Sites. However, you can connect your site to GitHub if you have [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) installed on your site. * Once you connect your site to GitHub be careful not to delete your site's repo or revoke the Velo app's access to this repo. These actions may cause your site's GitHub connection to stop working even if you restore the repo or reinstall the app.
If you haven't already done so, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding** (Wix Studio), or enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site) (Wix Editor). 1. Go to the GitHub Integration section of your editor: + **Wix Studio:** In the Code sidebar, click **GitHub** then **Connect to GitHub**. ![Connect to GitHub](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2975acc2f04e09b584d7445e64fb1107.png) + **Wix Editor:** Click the **GitHub Integration** ![](https://user-images.githubusercontent.com/89579857/184818043-6a791df6-9f83-4183-ba2f-63d8c32a6cf7.png) icon in the Code sidebar and then **Connect to GitHub**. 3. Click **Continue**. ![](https://user-images.githubusercontent.com/89579857/188311147-436ecd17-c717-440c-93ae-feb7685eddeb.png) 4. Click **Sign In**. Follow the prompts to sign in to your GitHub account and authorize Velo to create a new repo for you. 5. Choose an owner and enter a name for the new repo to connect to your site. Optionally, you can add a custom description as well. Click **Create**. ![](https://user-images.githubusercontent.com/89579857/188311195-229312f8-3fd5-4c6d-902d-b746f0c9915d.png) 6. Click **Install** to start installing the Velo GitHub app on your new repo. This app allows your site to communicate with GitHub. You may need to authenticate your identity in GitHub before continuing. 7. Make sure that both **Only select repositories** and your new repo are selected. Click **Approve and Install**. ![](https://user-images.githubusercontent.com/89579857/184847450-8f0b382d-4dfd-4918-a23c-983e910a8fc2.png) 8. Copy the displayed terminal commands. You need them for the next section of the setup. 9. Click **Close**. Your repo name and default branch are displayed in the **GitHub Integration** panel. ![](https://user-images.githubusercontent.com/89579857/188308239-bb8c9c44-4844-470a-b1d5-dbd0d48be4ce.png) Once you connect your site to GitHub, your editor enters [read-only mode](https://support.wix.com/en/article/velo-working-with-editor-x-and-github-integration#about-read-only-mode). You can always choose to [disconnect your site from GitHub](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/disconnect-your-site-from-git-hub.md). ## Step 2 | Set up your local environment Once your site's repo is set up, you can clone it to your computer and install the Wix CLI for Sites. The commands used in this procedure accomplish the following: * Clone your site's repo to your computer. * Install the repo's dependencies. * Install the [Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) globally on your computer. To set up your local environment, do the following: 1. Open the terminal on your computer. 2. Navigate to the directory where you want to store your repo's files. 3. Run the commands you copied down in the previous section. Depending on your computer's setup, you may need administrator privileges to run the commands. To see the commands again, open your site's editor and click **Local Dev Setup**. ![](https://user-images.githubusercontent.com/89579857/188308276-c55c6e03-3bc4-4ca0-9512-14bc1d8040b4.png) The default commands use npm as the package manager for installations. You can also use yarn to manage packages. To do this, make the following changes: * Change `npm install` to `yarn install`. * Change `npm install -g @wix/cli` to `yarn global add @wix/cli`. After the installation, the [Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md) opens in your default web browser. 4. Open the repo in your favorite local IDE to start writing code. ## Step 3 | Set up the Wix CLI for Sites The CLI is a package that you install on your computer. 1. Open your computer's terminal. 2. Run `npm install -g @wix/cli` or `yarn global add @wix/cli`. Once you've installed the CLI globally, you can use it with any Wix site repos on your computer. ## Next Steps You're ready to start writing your site's code, and [testing it with the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/test-your-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Publishing a Site with Git Integration & Wix CLI for Sites ## Article: Publishing a Site with Git Integration & Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/publishing-a-site-with-git-integration-wix-cli-for-sites.md ## Article Content: # Publish a Site with Git Integration & Wix CLI for Sites Once you've used [Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) to edit your site's code and test it, you're ready to move it to your live site. This process includes 2 stages: 1. Pushing your code to your site's GitHub repository. 2. Publishing your site. ## Step 1 | Push your code to GitHub Your site's editor is synced with the [default branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch) of your site's GitHub repository. Usually, this is the `main` branch, but you can [change it](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/changing-the-default-branch) to any branch you want. To sync your code with the editor, push a commit to your repo's default branch. You can use other GitHub features like branches, pull requests, and reviews as you normally would.
Once you push a commit to your default branch, your code appears in the editor. The commit details appear in the **GitHub Integration** ![](https://user-images.githubusercontent.com/89579857/184650610-e5aec718-676b-4dc5-b9b4-cad2b6c1fc64.png) panel of the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md) (Wix Editor), or in the **Github** section of the Code sidebar (Wix Studio).
>**Note:** > There may be a delay between when you push a commit to GitHub and when it appears in the editor. ## Step 2 | Publish your site When working with Git Integration & Wix CLI for Sites, you can publish your site either from the editor or from the [Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md). The different publishing options use different copies of your site's code and different [UI versions](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md), as follows: |Publishing Method |Code Version |UI Version | |---|---|---| | Editor | The code in the default branch of your site's repo. | The latest UI version, even if this version doesn't align with the code in your site's repo. | | CLI - Latest commit | The code in the default branch of your site's repo. | The UI version indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/git-hub-repository-file-structure.md) file in your site's repo. | | CLI - Local code | The code saved in your IDE even if you haven't pushed it to GitHub. | The UI version indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/git-hub-repository-file-structure.md) file in your site's repo. | ### Publish from the Editor To publish your site from the editor do the following: 1. Open the editor. 2. Click **Publish**. ![](https://user-images.githubusercontent.com/89579857/188307221-d43f33ad-36db-4e48-952e-181c619e7891.png) 3. Note that a modal may appear warning you that you are about to publish the latest UI version. Click **Continue**. ### Publish from the Wix CLI for Sites To publish your site from the Wix CLI for Sites, do the following: 1. Open your terminal and navigate to your site's repo. 2. Run this command: `wix publish`. 3. When you publish from the CLI, you can either use the code in your repo's default branch, or the code saved in your IDE. ![](https://user-images.githubusercontent.com/89579857/186160822-6feb52bf-bfb4-42e8-a4c1-5cfe03d16449.png) * To publish from your default branch, select `Latest commit from {your repo's default branch}`. * To publish from the code on your local environment, select `Local code`. **Important:** Publishing your local code leaves your live site and your site's GitHub repo out of sync. If you publish from the repo later without pushing your local code, your local code is overwritten and may be lost. 4. Once you make a selection, the source of the code and UI version that are being used to publish are displayed in the terminal. A link to a preview site is also displayed. If you're sure you want to publish, press `y`. The terminal displays the URL of your published site and links to view logs and backend traffic. ![](https://user-images.githubusercontent.com/89579857/186163253-d5f4a952-9aab-4561-a81e-8f00d4184f1f.png) ## See also * [Wix CLI for Sites commands](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) * [About Automated Workflows and the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-automated-workflows-and-the-wix-cli-for-sites.md) * [Fix a Broken Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/fixing-a-broken-git-hub-integration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Fixing a Broken GitHub Integration ## Article: Fixing a Broken GitHub Integration ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/fixing-a-broken-git-hub-integration.md ## Article Content: # Fix a Broken Git Integration [Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) relies on the [Velo GitHub App](https://github.com/apps/velo-app) to maintain the connection between your site and its GitHub repository. If the app is uninstalled or suspended, your site's Git integration stops working. Your Git integration can also stop working if you make changes to the repo's settings or to your GitHub account settings. When the connection between your site and GitHub is lost, a modal indicating the problem appears in the editor. The following sections explain each of the modals you might see in the editor, and the steps you can take to fix your Git integration. ## Repository Not Found The **GitHub repository cannot be found** modal indicates that the Velo GitHub App can't find your site's repo. This happens in the following cases: * You deleted the repo. * You changed the repo's name. * You transferred the repo to another GitHub account or organization. * You changed your GitHub username. * You revoked the Velo App's access to the repo. In the first 4 cases, you need to disconnect your site from GitHub. You can do this by clicking the **Disconnect From GitHub** button in the modal. Once you've done this, [connect your site to GitHub](https://support.wix.com/en/article/velo-setting-up-git-integration-wix-cli-early-access#connect-your-site-to-github) again and create a new repo for your site. Please note that you can't reconnect your site to the original repo. In the last case, you can restore the connection to your site's repo by granting the Velo App access to it: 1. Open [GitHub](https://www.github.com) in a new tab in your browser. 2. In the upper-right corner, click your profile photo and select **Settings**. ![](https://user-images.githubusercontent.com/89579857/211188090-bc05a823-0b0c-462d-bdd2-8099d26dd28a.png) 3. On the left sidebar, under **Integrations**, click **Applications**. ![](https://user-images.githubusercontent.com/89579857/211188102-63a97ac5-5c3e-4fd7-a97c-c91a0f33ab58.png) 4. With the **Installed GitHub Apps** tab selected, find the Velo App and click **Configure**. 5. In the **Repository access** section, click **Select repositories**. Select your site's repository from the dropdown list. Click **Save**. 6. Refresh the Wix editor. ## Velo App Uninstalled The **Velo App on GitHub was uninstalled** modal indicates that you have uninstalled the Velo App from your GitHub account. To reinstall the app, take the following steps: 1. Open the [Velo App](https://github.com/apps/velo-app) page in a new tab in your browser. 2. Click **Configure**. 3. Select where you want to install the app. 4. Select **Only select repositories** and select your site's repository from the dropdown list. 5. Click **Install**. 6. Refresh the Wix editor. ## Velo App Suspended The **Velo App on GitHub was suspended** modal indicates that you have suspended the Velo App's access to your GitHub account. To unsuspend the app, take the following steps: 1. Open [GitHub](https://www.github.com) in a new tab in your browser. 2. In the upper-right corner, click your profile photo and select **Settings**. ![](https://user-images.githubusercontent.com/89579857/211188090-bc05a823-0b0c-462d-bdd2-8099d26dd28a.png) 3. On the left sidebar, under **Integrations**, click **Applications**. 4. With the **Installed GitHub Apps** tab selected, find the Velo App and click **Configure**. 5. In the **Danger Zone** section, click **Unsuspend**. 6. Refresh the Wix editor. ## See also * [Test Your Code in the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md) * [Publish a Site with Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix CLI for Sites Commands ## Article: Working with the Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md ## Article Content: # Wix CLI for Sites Commands This article documents the CLI commands for working with your Wix site. For a detailed explanation of the process and how to initially set up the Wix CLI for Sites, see [Set Up the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/setting-up-git-integration-wix-cli.md#step-3--set-up-the-wix-cli). ## Command Overview | Command | Description | |--------------------|--------------------------------------------------------------| | [wix dev](#wix-dev) | Opens a local development environment for your Wix site. | | [wix install](#wix-install)| Installs a package to your site. | | [wix update](#wix-update) | Updates a package installed on your site. | | [wix uninstall](#wix-uninstall) | Uninstalls a package from your site. | | [wix publish](#wix-publish)| Publishes your site to production. | | [wix preview](#wix-preview)| Creates a shareable version of your site before going live. | | [wix login](#wix-login) | Logs in to your Wix account. | | [wix whoami](#wix-whoami) | Displays the username of the logged-in Wix user. | | [wix logout](#wix-logout) | Logs out of your Wix account. | ## wix -h ``` bash wix -h ``` Displays a list of supported CLI commands in the terminal. You can also run `wix [command] -h` to get detailed help for a command. ## wix dev ```bash wix dev ``` Opens a site's Local Editor. ### wix dev flags | Flag | Description | |-----------------|--------------------------------------------------------------| | `--tunnel` | Use this flag to allow the editor to connect to the CLI when running it on a cloud IDE. | ## wix install ```bash wix install [options] ``` Installs a code package to a site's repo. If your site's repo has a `yarn.lock` file, the installer uses yarn to install packages by default. Otherwise, the installer uses npm by default. To specify which package manager to use, include an `--npm` or `--yarn` options flag. Example install command: ```bash wix install --yarn marked ``` Once the package is installed, a message is displayed in the terminal. ```bash marked@4.0.16 successfully installed. ``` ### wix install flags | Flag | Description | |-----------------|--------------------------------------------------------------| | `--npm` | Forces the installer to use npm as the package manager. | | `--yarn` | Forces the installer to use yarn as the package manager. | ## wix update ```bash wix update [options] ``` Updates a code package installed on your site's repository. ## wix uninstall ```bash wix uninstall [options] ``` Uninstalls a code package from your site's repository. ## wix publish ```bash wix publish ``` Publishes your site. You can choose to use either the code in the default branch of a site's repo or the local code in your IDE as the publishing source. ```bash ? What would you like to publish? › - Use arrow-keys. Return to submit. ❯ Latest commit from origin/main Local code ```
**Important:** Publishing your local code leaves your live site and your site's GitHub repo out of sync. If you publish from the repo later without pushing your local code, your local code is overwritten and may be lost.
For more information, see [Publish a Site with the Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md). ## wix preview ```bash wix preview ``` Builds a preview version of a site. You can only use this command if you have previously published your site. You can build the preview from the code in the default branch of a site's repo or from the local code in your IDE. ```bash ? What would you like to preview? › - Use arrow-keys. Return to submit. ❯ Latest commit from origin/main Local code ``` Once the preview site is ready, a URL to the site is displayed in the terminal. ```bash Creating preview deployment... Your preview deployment is now available at http://wix.to/JOaBagO ``` > **Note:** > * This preview isn't a [test site](https://support.wix.com/en/article/about-test-sites) and doesn't appear in the Release Manager. > * The preview uses the live versions of any [HTTP functions](https://www.wix.com/velo/reference/wix-http-functions) exposed on your site. This means you can't use the preview to test changes to HTTP functions. To test HTTP functions, commit and push them to your site's repo and use [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) in the editor. ## wix login ```bash wix login ``` Logs in to a Wix account. This allows the CLI to perform actions on that account's sites. ## wix whoami ```bash wix whoami ``` Displays the username of the currently logged-in Wix user. ## wix logout ```bash wix logout ``` Logs you out of your Wix account. ## See also * [About Automated Workflows and the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-automated-workflows-and-the-wix-cli.md) * [Testing your code with the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Local Editor ## Article: Working with the Local Editor ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md ## Article Content: # About the Local Editor > **Note** > This article refers to 2 different versions of the editor: > > * **Regular Editor**: The editor that's generally used to build Wix sites. > * **Local Editor**: A specialized version of the Regular Editor that's used when developing your site in your local IDE. The Local Editor is a tool for testing code added to your site in your local IDE using [Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md). You can use the Local Editor to test your code in real time and make changes to your site's design that you can sync with your IDE. For detailed instructions on this flow, see [Test Your Code in the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md). ## Differences in the Local Editor Working in the Local Editor is different from working in the Regular Editor. Please note the following changes: * The **Publish** button is replaced with the **Save** button. * Code files are read-only. To make changes to your site's code, use your local IDE. * Packages & Apps are read-only. To add, update, or delete packages and apps, use your local IDE. * The Wix IDE is disabled, use your local IDE instead. * If you duplicate a page on your site, the page's code isn't included in the copy. * Changes to [collection fields](https://support.wix.com/en/article/content-manager-about-your-collection-fields) are immediately reflected on your live site, even before you publish. ## Making changes to site design The Local Editor allows you to make changes to your site's design such as adding or modifying elements and pages. These changes generate a new UI version for your site which you need to sync with your IDE. Once the UI version is synced with your IDE, code files for newly added pages appear in your repo. Autocomplete is also enabled for any newly added page elements. ### About UI versions UI versions are snapshots of a site's design. Saving any design changes to a site including adding a new page or adding or modifying elements generates a new UI version. UI versions are generated in both the Local Editor and the Regular Editor. Wix uses UI versions to match the code in your IDE with your site's design. The current UI version for the code in your site's repo is indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/git-hub-repository-file-structure.md) file.
## See also * [Test Your Code in the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Changes to the Editor When Your Site Is Integrated ## Article: Integrating Your Site With GitHub ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/changes-to-the-editor-when-your-site-is-integrated.md ## Article Content: # Changes to the Editor When Your Site Is Integrated When you connect your site to GitHub, a GitHub repository is created to store your site's code. Once this happens, the code in the editor is synced with the [default branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch) of this repo. The name of the connected repo as well as the details of commits to the default branch appear in the **GitHub Integration** ![](https://user-images.githubusercontent.com/89579857/184650610-e5aec718-676b-4dc5-b9b4-cad2b6c1fc64.png) panel of the sidebar.
## About read-only mode While your site is connected to GitHub, the editor is in read-only mode. This involves the following changes: * Code files are read-only. You can't make any changes to your site's code or add new files. Use your local IDE to [add new files and change code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/git-hub-repository-file-structure.md). * You can add [dynamic event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) to elements using code. To see the event handlers supported for each element, check the [Velo API Reference](https://www.wix.com/velo/reference/$w). * Changes to the [fields](https://support.wix.com/en/article/content-manager-about-your-collection-fields) in a collection are immediately reflected on your live site, even before you publish. * If you duplicate a page on your site, the page's code isn't included in the copy. ## See also * [Testing your code with the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md) * [Publish a Site with Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: GitHub Repository File Structure ## Article: Understanding Your Site's GitHub Repository ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/git-hub-repository-file-structure.md ## Article Content: # GitHub Repository File Structure The repo's file structure matches the [public](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#public), [backend](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#backend), and [page code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#page-code) sections in the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md) (Wix Editor). The file structure includes these important elements: * The [wix.config.json](#wixconfigjson) file found in the repo's root folder. * The **src** folder, that contains the following: * The [Backend folder](#backend-folder) * The [Pages folder](#pages-folder) * The [Public folder](#public-folder) * The [velo.dependencies.json](#velodependenciesjson) file (only exists once your first npm package is installed) Add your code in either the **Pages**, **Backend**, or **Public** folders. Files or folders added to the root of the **src** folder are ignored. >**Notes:** > The following Velo features can't be added to a site when using Git Integration & Wix CLI for Sites: > * [Custom Extensions (Beta)](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) > * [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) ## Pages folder This folder contains code files for each of the pages on your site as well as the [masterpage.js](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#global-site) file. The code you add to these files runs when visitors open pages on your site. These files correspond to the ones found in the [**Main Pages**](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#main-pages) section of the **Page Code** ![](https://user-images.githubusercontent.com/89579857/184645988-6d4ee6d3-34ab-45bc-b914-5779a7de0cad.png) tab in the Code sidebar. When you add a page to your site in a Wix editor in your browser, a code file for that page gets added to your repo. The name of the file has 2 components: the name of the page that you define in the editor, and an internal ID string. The sections are separated by a period. ![](https://user-images.githubusercontent.com/89579857/188305074-6e2ee718-13b8-435d-9c75-bcb126f35718.png) When you [add a dynamic page](https://support.wix.com/en/article/content-manager-about-dynamic-pages#adding-dynamic-pages) to your site 2 code files are added to the site's repo corresponding to the dynamic list and dynamic item pages. When you open a page's code file, you see the same sample code that appears in these code files in Wix editors in your browser. ![](https://user-images.githubusercontent.com/89579857/184646571-1e14f166-2b86-4f21-bf57-83468251bca8.png) When you delete a page in a Wix editor in your browser, the page's corresponding code file is deleted from your repo. >**Notes:** > * You can't create new code files for pages from your IDE. To add a file, create a new page for your site in a Wix editor in your browser, and [sync](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md) your site with your local IDE. > * Do not rename code files for pages. Wix uses these file names to associate the files with the appropriate pages on your site. If you rename a file, your code is ignored and a new code file is created for the page. ## Backend folder This folder contains the backend code files for your site. These files correspond to the ones found in the [**Backend**](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#backend) section of the **Public & Backend** tab in the Code sidebar. Add the following files to this folder to include them in your site: * [**Web Modules:**](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) These are files that allow you to expose functions in your site's backend that you can run in your frontend code. These files require a `.web.js` file extension. > **Note:** For web modules with a `.jsw` extension, permissions are managed using the [permissions.json](#permissionsjson) file. Note that `.jsw` web modules are deprecated, although still supported for backward compatibility for both Wix Editor and Wix Studio. * **data.js:** A file for [adding data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md) to your site's collections. * **routers.js:** A file for implementing [routing and sitemap](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) functionality for your site. * **events.js:** A file for implementing your site's [backend event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md). * **http-functions.js:** A file for implementing [HTTP endpoints](https://www.wix.com/velo/reference/wix-http-functions/introduction) that are exposed on your site. * **jobs.config:** A file for [scheduling recurring jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md). Jobs consist of other backend code that's run at regular intervals. * **General backend files:** JavaScript code files. You can import code from these files into any other backend file on your site. These files require a `.js` file extension. Use the following syntax to import code from backend files: ```javascript import { myFunctionName } from 'backend/myFileName'; ``` Trying to import from the relative path in your site's repo doesn't work. ### permissions.json
When using `.jsw` (deprecated) web modules, you can't change permissions in the editor when using Git Integration & Wix CLI for Sites. Instead, use the permissions.json file to set function permissions.   The backend folder also contains the **permissions.json** file. This file defines [permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) for the functions in your web module files. The file contains a key, `"web-methods"`, whose value is an object that contains keys named after the web module files in your backend folder. Name these keys with the following syntax: `"backend/{path to file}/myFile.jsw"`. The value for each file name key is an object that contains keys named after the functions in that file. Each function key has a value with the following format: ```json "myFunction": { "siteOwner" : { "invoke" : // Boolean }, "siteMember" : { "invoke" : // Boolean }, "anonymous" : { "invoke" : // Boolean } } ``` These values reflect the different levels of web module function permissions. You should set them using the following options: * **Owner-only access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `false` * `anonymous.invoke` : `false` * **Site member access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `true` * `anonymous.invoke` : `false` * **Anyone can access**: * `anonymous.invoke`: `true` * `siteMember.invoke` : `true` * `anonymous.invoke`: `true` The `"web-methods"` object must also contain a `"*"` key. The value for this key defines the default permissions that are applied to any function whose permissions you don't set manually. Here is an example `permissions.json` file for a site with a backend file called `helperFunctions.jsw`. The file's functions are called `calculate`, `fetchData`, and `syncWithServer`. In this case anyone can call `calculate`, site members can call `syncWithServer`, and only site owners can call `fetchData`. ```json { "web-methods": { "*": { "*": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } } }, "backend/helperFunctions.jsw": { "calculate": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } }, "fetchData": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": false }, "anonymous": { "invoke": false } }, "syncWithServer": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": false } } } } } ```
## Public folder This folder contains the public code files for your site. These files correspond to the ones found in the [**Public**](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md#public) section of the **Public & Backend** tab in the Code sidebar. You can import code from these files into any other file on your site. Use the following syntax to import code from public files: ```javascript import { myFunctionName } from 'public/myFileName'; ``` Trying to import from the relative path in your site's repo doesn't work. ## velo.dependencies.json This file is created automatically when you install your first npm package. Wix uses this file to track the [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) installed on your site. The file is updated automatically when you [install a package](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) with the Wix CLI for Sites. Don't change this file manually. ## wix.config.json Wix uses this file to associate your repo's code with a particular site and [UI version](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md). This file is updated automatically when your repo is synced with the Wix editors. Don't change this file manually. ## See also * [Publish a Site with Git Integration & Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md) * [Test Your Code in the Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/test-your-code-in-the-local-editor.md) * [Fix a Broken Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/fixing-a-broken-git-hub-integration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Automated Workflows and the Wix CLI for Sites ## Article: About Automated Workflows and the Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-automated-workflows-and-the-wix-cli-for-sites.md ## Article Content: # About Automated Workflows and the Wix CLI for Sites You can use the Wix CLI for Sites in your preferred automated workflow environment by [authenticating it with an API key](https://support.wix.com/en/article/about-wix-api-keys). For example, you can create [GitHub Actions](https://docs.github.com/en/actions) workflows to streamline and automate site development tasks, such as creating preview deployments and publishing updates to your site. Learn how to [Set Up GitHub Actions to Work with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/set-up-git-hub-actions-to-work-with-the-wix-cli.md). ## Suggested workflows Setting up GitHub Actions workflows to work with the Wix CLI for Sites allows you to streamline and automate site development procedures. Some suggested workflows include: 1. Create a preview deployment: Build a live, shareable preview version of your site whenever a new pull request is opened, based the updated code in the pull request's branch. 2. Publish when pushing changes: Automatically publish your site whenever changes are pushed to the default branch of your site's repo. ## See also - [Set Up GitHub Actions to Work with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/set-up-git-hub-actions-to-work-with-the-wix-cli.md) - [Learn more about GitHub Actions](https://docs.github.com/en/actions) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Set Up GitHub Actions to Work with the Wix CLI for Sites ## Article: Set Up GitHub Actions to Work with the Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/set-up-git-hub-actions-to-work-with-the-wix-cli-for-sites.md ## Article Content: # Set Up GitHub Actions to Work with the Wix CLI for Sites To enable a GitHub Actions workflow to perform actions on your Wix site, the workflow must set up and authenticate the Wix CLI for Sites using an API key. ## Prerequisites Before creating a GitHub Action workflow to interact with your site, make sure that you [integrate your site with GitHub](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/changes-to-the-editor-when-your-site-is-integrated.md). > **Note:** You can also [Set up GitHub integration with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/setting-up-git-integration-wix-cli-for-sites.md) locally. ## Set up GitHub Actions to work with your site To authorize a GitHub Actions workflow to interact with your site, follow these steps: ### Step 1 | Generate an API key An API key allows an authorized external client to access your Wix account and site data. With an API key, you can authorize GitHub Actions to [execute Wix CLI for Sites commands for your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md). You can generate an API key in the [API Keys Manager](https://manage.wix.com/account/api-keys). Select the **Wix CLI for Sites - Git Integration** site permission to allow GitHub Actions to use the Wix CLI for Sites for your sites. ### Step 2 | Store the API key as a GitHub secret Visit your site's GitHub repo and [create a repository secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) to store the API key. This makes your API key available to use securely in your repo's GitHub Actions workflows. ### Step 3 | Set up the Wix CLI for Sites in your GitHub Actions workflow Configure your GitHub Actions workflow to set up and authenticate the Wix CLI for Sites: 1. In your GitHub Actions workflow, [set up the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/setting-up-git-integration-wix-cli-for-sites.md) by including the following command: ```yaml npm install ``` 2. Next, authenticate it by including the following command: ```yaml npm run wix login -- --api-key ${{ secrets.WIX_CLI_API_KEY }} ``` ### Step 4 | Include Wix CLI for Sites commands Your workflow can now run authenticated Wix CLI for Sites commands to interact with your site. For example: - Include `npm run wix preview` in your workflow to [build a preview version of the site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) based on the current branch. You can set up a workflow to create a preview version whenever a new PR is created. - Include `npm run wix publish` in your workflow to [publish the site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) based on the repo's default branch. You can set up a workflow whenever a PR is merged to the default branch. ## See also - [About Automated Workflows and the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-automated-workflows-and-the-wix-cli-for-sites.md) - [Working with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/wix-cli-for-sites-commands.md) - [Generate an API Key](https://dev.wix.com/docs/go-headless/getting-started/setup/authentication/generate-an-api-key-for-admins.md) - [Using Secrets in GitHub Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Disconnect Your Site From GitHub ## Article: Disconnect Your Site From GitHub ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/disconnect-your-site-from-git-hub.md ## Article Content: # Disconnect Your Site From GitHub If you need to, you can disconnect your site from GitHub. When you do this, the site retains the code from the most recent commit to its GitHub repo's default branch. After disconnecting, the repo still exists in GitHub but changes to it aren't reflected on your site. Once your site is disconnected from GitHub, the editor exits read-only mode.
**Important:** Once you disconnect your site from GitHub and from a repo, you can't reconnect the site to that repo again. If you reconnect your site to GitHub later, a new repo is created.
To disconnect from GitHub: 1. Click the **GitHub Integration** icon on the sidebar. ![](https://user-images.githubusercontent.com/89579857/184849141-33a64b0c-ff1a-463b-b0b9-a5051561a38f.png) 2. Hover over your repo's name and click the More Actions icon. ![](https://user-images.githubusercontent.com/89579857/185401800-274af867-f16b-4746-87c1-84c55f60961b.png) 3. Select **Disconnect from GitHub**. ![](https://user-images.githubusercontent.com/89579857/188308466-bc1f1441-9ecb-433a-93b1-6dcebe568c0e.png) 4. In the modal, click **Disconnect from GitHub**. ![](https://user-images.githubusercontent.com/89579857/188308423-38263af6-d191-460e-8166-3abc601a293d.png) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Test Your Code in the Local Editor ## Article: Test Your Code in the Local Editor ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/test-your-code-in-the-local-editor.md ## Article Content: # Test Your Code in the Local Editor > **Note** > This article refers to 2 different versions of the editor: > > * **Regular Editor**: The editor that's generally used to build Wix sites. > * **Local Editor**: A specialized version of the Regular Editor that's used when developing your site in your local IDE. The [Local Editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md) is a tool for testing code added to your site in your local IDE using [Git Integration & Wix CLI for Sites](https://support.wix.com/en/article/velo-about-velo-pro). You can use the Local Editor to test your code in real time and make changes to your site's design that you can sync with your IDE. This article explains how to test your code in the Local Editor and sync any design changes you make. ## Step 1 | Open the Local Editor Open the Local Editor from your computer using the [Wix CLI for Sites](https://support.wix.com/en/article/velo-working-with-the-wix-cli-beta). 1. Open your computer's terminal and navigate to the clone of your site's GitHub repository. 2. Run this command: `wix dev`. A new tab with the Local Editor opens in your default browser. Notice the **Local Editor** in the top bar on the left. ![Local Editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/cd9d208a8e66c978cc169e7562f5a520.png) > **Note:** If you're running the CLI on a cloud provider's IDE, such GitHub Code Spaces, add the `--tunnel` flag to the `wix dev` command. This makes the full command `wix dev --tunnel`. ### Reopen a closed Local Editor The Local Editor needs both the terminal and a browser tab open to run. If you close the browser tab, your terminal displays a URL you can use to reopen the editor in your browser. You can also do this by pressing `e` in the terminal.
If you close your terminal, the browser displays this screen:
In this case, you need to [open the Local Editor](https://support.wix.com/en/article/velo-working-with-the-local-editor-beta#open-the-local-editor) again. ## Step 2 | Test your code Once the Local Editor is open, you can use it to test your code in real time. 1. Edit your site's code in your IDE and save it. ```js $w.onReady(function () { console.log("Hello World!"); }); ``` Your code is automatically synced with Local Editor. You can see it in the editor's code panel. ![](https://user-images.githubusercontent.com/89579857/184881543-76c28381-97c3-40f7-bd58-517b15212b11.png) 2. In the Local Editor, click **Preview**. Your site code runs. >**Note:** > Even though you can see your code changes in the Local Editor, they don't appear in the Regular Editor until you [push them to GitHub](https://support.wix.com/en/article/velo-publishing-a-site-with-velo-pro#push-your-code-to-github). Changes don't appear on your live site until you [publish](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md#publish-your-site). ### Sync design changes to your IDE If your testing reveals the need for design changes, you can make the changes directly in the Local Editor. The Local Editor and Regular Editor are synced, so any changes made in one are immediately reflected in the other. However, they aren't reflected in the code in your IDE. To sync the changes with your IDE, do the following: 1. Click **Save**. ![](https://user-images.githubusercontent.com/89579857/188309927-346c82b0-7696-45f5-be51-bb45419f9bd7.png) Clicking this button creates a new [UI version](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-the-local-editor.md). 2. If you see this modal, click **Continue**. ![](https://user-images.githubusercontent.com/89579857/188310497-a2e0d744-5539-4dd2-8cd8-ada8954b788a.png) ## Next steps When you're happy with the way your code runs, [push it to GitHub and publish your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/publishing-a-site-with-git-integration-wix-cli.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Secrets Manager ## Article: About the Secrets Manager ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md ## Article Content: # About the Secrets Manager The [Secrets Manager](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/secrets-manager) allows you to securely store and manage secrets. A secret is any piece of sensitive information used in your site's code, such as API keys, OAuth tokens, and environment configuration settings. [Add secrets](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/manage-secrets-in-the-secrets-manager.md#add-a-secret) to the Secrets Manager in your dashboard by assigning each secret a [unique name](#secret-fields). You can then use that name to retrieve the secret in code using the [Secrets API](https://dev.wix.com/docs/velo/api-reference/wix-secrets-backend-v2/introduction.md). ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/about-the-secrets-manager-md_velo-articles_developer-tools_images_secrets-list-2.png) ## Enhanced security Hardcoding secrets into your code can create security risks for your site, which can lead to data breaches and unauthorized access to your site. Here's how the Secrets Manager improves your site's security: * **Code sharing**: You may want to collaborate and share your code with others or manage it in a public repository, increasing the potential for accidentally leaking secrets. * **Code reusability**: Since code is often reused, your hardcoded secrets may be copied, increasing risk of exposure. ## Secret fields It’s important to understand the rules and limitations of a secret within the Secrets Manager. | Field name | Field description | Requirements | Required field | |-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------| | Name | ID you assign to the secret. Access it in code using the [Secrets API](https://dev.wix.com/docs/velo/api-reference/wix-secrets-backend-v2/introduction.md). |
  • 40 character maximum. Only letters (a-z) and numbers.
  • No spaces or special characters except hyphens (`-`) and underscores (`_`).
  • Cannot begin with `wix`, regardless of case.
| Yes | | Value | Value of the secret. |
  • 3,500 character maximum.
| Yes | | Description | Information about the secret. |
  • 200 characters maximum.
| No | ## Roles and permissions Only site collaborators with the general role of **Admin (Co-Owner)** or **Website Manager** have access to the Secrets Manager. Learn more about [roles and permissions](https://support.wix.com/en/article/roles-permissions-overview). > **Important:** > - [Transferring a site](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) results in secrets being transferred to the new owner. > - [Duplicating a site](https://support.wix.com/en/article/duplicating-your-site-1472847?tabs=Dashboard) **doesn't** result in secrets being transferred to the duplicate site. ## See also - [Manage Secrets in the Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/manage-secrets-in-the-secrets-manager.md) in the Secrets Manager. - [About the Wix Secrets API](https://dev.wix.com/docs/velo/api-reference/wix-secrets-backend-v2/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Manage Secrets in the Secrets Manager ## Article: Working with the Secrets Manager ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/manage-secrets-in-the-secrets-manager.md ## Article Content: # Manage Secrets in the Secrets Manager The [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) allows you to access and manage secrets securely, and use them in your site's code.
## Step 1 | Access the Secrets Manager There are multiple ways to access the [Secrets Manager](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/secrets-manager), depending on which interface you are using. From **Wix Studio or Wix Editor**: 1. Navigate to **Developer Tools** in the code panel. 2. Click on **Secrets Manager** in the **Security** section. From the **dashboard:** 1. Navigate to **Developer Tools** in the dashboard menu. 2. Click on **Secrets Manager**. ## Step 2 | Manage your secrets Use the Secrets Manager to add, edit, and delete secrets in the Secrets Manager. ![alt text](https://wixmp-833713b177cebf373f611808.wixmp.com/images/working-with-the-secrets-manager-md_velo-articles_developer-tools_images_manage-secrets-ui.png) > **Note:** Changes in the Secrets Manager are applied immediately and don't require publishing the site.
Add a secret To add a secret: 1. At the top right of the Secrets Manager, click **Add Secret**. 2. In the **Add secret** modal, fill in the [fields](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md#secret-fields). ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/working-with-the-secrets-manager-md_velo-articles_developer-tools_images_secret_add_panel.jpg) 3. Click **Add Secret**. The secret is added and stored in the Secrets Manager. The name and description are displayed, and the secret value is hidden. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/working-with-the-secrets-manager-md_velo-articles_developer-tools_images_secret-added.png)
Edit a secret To edit a secret:
Warning: Changing a secret's name or value breaks all code that implements the secret.
1. For the secret you want to edit, select **Edit** in the secret's **More Actions** home-icon menu. The **Edit secret** modal appears. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/working-with-the-secrets-manager-md_velo-articles_developer-tools_images_edit-secrets-modal-2.png) 2. - To change the secret's **Name** or **Description**, click the [fields](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md#secret-fields) to edit the text. - To change the secret's value, click **Retrieve value**, then click inside the secret's **Value** field that appears. 3. Click **Change Secret**.
Delete a secret To delete a secret:
Warning:
  • Deleting a secret breaks all code that implements the secret.
  • You cannot restore a deleted secret.
1. For the secret you want to delete, select **Delete secret** in the secret's **More Actions** home-icon menu. The **Delete secret?** modal appears. 2. Click **Delete Secret** to confirm. The secret is removed from the Secrets Manager.
## See also - [About the Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) - [About the Wix Secrets API](https://dev.wix.com/docs/velo/api-reference/wix-secrets-backend-v2/introduction.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Logs ## Article: About Wix Logs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md ## Article Content: # About Logs Logs provide information about events and errors that occur on your site. They are generated in both preview mode and for the published site, and can originate from your site's frontend and backend.
Use logs to: - Trace visitor flows. - Gain insights into your site's performance. - View messages generated by methods such as `console.log()` or `console.info()`. - Trace and resolve errors. Error logs include a stack trace to make debugging easier. - Compare logs against your [site history](https://support.wix.com/en/article/viewing-and-managing-your-site-history) and the [Release Manager](https://support.wix.com/en/article/accessing-the-release-manager) to debug and analyze different site revisions. > **Note**: Verbose logging is only supported for [routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md). ## Log viewing options Logs are available directly in your site dashboard, as well as through external monitoring tools you can connect to your site. You can view your site's logs with: - **Wix Logs:** Use [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) to view basic log information in real-time directly in the site dashboard. - **Google Cloud Logs:** View your [site's logs in Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md), a monitoring tool that performs log analysis and generates visual depictions of log data. - **An external monitoring tool:** View your [site's logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) of your choice to generate metrics and analyze logs. ## Log structure Each log contains the payload message as well as log metadata, such as the log's timestamp, origin, the site's [view mode](https://support.wix.com/en/article/studio-editor-saving-previewing-and-publishing-your-site#previewing-a-site), and the site's [revision number](https://support.wix.com/en/article/viewing-and-managing-your-site-history). Learn more about the [log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md). ## See also - [View logs using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) - [View logs using Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [View logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) - [Tutorial | Generate a log to debug your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) - [Log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: View Logs using Wix Logs ## Article: Viewing Live Wix Logs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md ## Article Content: # View Logs using Wix Logs Wix Logs allow you to view [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) in real-time, and you can pause, resume, or clear the log stream. Logs contain [metadata](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) as well as the log's payload message.
> **Notes**: > > - Logs viewed in Wix Logs can't be saved. To save logs for later use, use [Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) or another [external logging tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md). > - You can also use the [Site Monitoring service plugin](https://dev.wix.com/docs/velo/api-reference/wix-site-monitoring/service-plugin-spi/site-monitoring/introduction.md) to examine Wix Logs in real time. The [`LogEntry` object](https://dev.wix.com/docs/velo/api-reference/wix-site-monitoring/service-plugin-spi/site-monitoring/log-entry/introduction.md) contains the data for each log. ## View the logs 1. In the site dashboard, click **Developer Tools** > **Logging Tools**. 1. In the **Wix Logs** section, click **Open**. The Wix Logs page opens.
1. To generate a log, preview or publish your site and [trigger an event that generates a log](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md). For example, if you have a button that, when clicked, logs the result of 2 multiplied numbers, click the button. The logs indicating success appear in the **Wix Logs** page:
Similarly, if not successful, the logs might display as follows:
**Tip:** If the live log stream does not appear, refresh your site while the Wix Logs page is open in another tab.
By default, each log displayed in the Wix Logs page includes the following fields: | Column | Description | [Log object](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) property | | ----------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **Level** | Log's severity level. Possible values are:

`INFO`, `WARNING`, `ERROR`, or `DEBUG`. | `severity` | | **Time** | Timestamp when the log was triggered. | `receiveTimestamp` | | **Message** | Log's payload message in JSON format. | `jsonPayload.message` | | **Source file** | Path to the source file that generated the log message. | `sourceLocation.file` | 1. You can change the view to suit your needs: - To change the order of columns or display additional ones, such as the operation ID and the data stream, click ![customize-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3f5bdbf487735a2fe93e9717218340ac.png). - Filter the log messages by: - **Stream**. Filter by preview, live site, or both. - **Level**. Limit the logs by info, warning, error, or debug. - Find specific logs by providing search criteria. 1. To view additional details for a log, hover over it and click **View**. See the [log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) for an explanation of each field. 1. To copy the object in JSON format, click ![ellipsis-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) and then **Copy JSON**. ## See also - [About Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [View logs using Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [View logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) - [Tutorial | Generate logs to debug a site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) - [Log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) - [Site monitoring service plugin](https://dev.wix.com/docs/velo/api-reference/wix-site-monitoring/service-plugin-spi/site-monitoring/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: View Logs using Google Cloud Logs ## Article: Connecting Wix Logs to Google Cloud Logs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md ## Article Content: # View Logs using Google Cloud Logs You can view your site's [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) using [Google Cloud Logs](https://cloud.google.com/products/operations), an external monitoring tool that displays, analyzes, and stores your logs. With Google Cloud Logs, you can: - Stream, search, and analyze log data in real time. - Store log data in the cloud. - Trace, sort, and filter aggregated error data. This article explains how to: - [Connect to Google Cloud Logs](#connect-to-google-cloud-logs) - [View your logs using Google Cloud Logs](#view-your-logs-using-google-cloud-logs) - [Disconnect Google Cloud Logs](#disconnect-google-cloud-logs) ## Connect to Google Cloud Logs To connect your site's logs to Google Cloud Logs: 1. In the site dashboard, click **Developer Tools** > **Logging Tools**. 2. In the Google Cloud Logs section, click **Open**. The Google Cloud Logs page opens.
3. In the Google Cloud Logs page, click **Connect**. The Google Account Login page appears. 4. Sign in to the Google account associated with your site. Your site is now connected to Google Cloud Logs. ## View your logs using Google Cloud Logs Reload your site and [generate logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) from its frontend or backend. To view logs using Google Cloud Logs: 1. In your site dashboard, click **Developer Tools** > **Logging Tools**. 2. In the Google Cloud Logs section, click **Open**. The Google Cloud Logs page appears.
3. Click **Open** and sign in to a Google account associated with your site. Once logged in, the Google Cloud Logs Explorer page opens. It displays your site's logs and errors.
Click any log to view its data.
To better understand the logs, learn about the [log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md). ## Disconnect Google Cloud Logs To disconnect your site from Google Cloud Logs: 1. In your site dashboard, click **Developer Tools** > **Logging Tools**. 2. In the **Google Cloud Logs** section, click **Open**. The Google Cloud Logs page appears. 3. On the top right of the page, click **Disconnect**.
4. In the **Disconnect from Google cloud Logs?** popup, click **Disconnect**.
Your site is now disconnected from Google Cloud Logs. > **Note:** Disconnecting your site from Google Cloud Logs does not affect the data stored in your Google Cloud Logs account. You can reconnect your site to Google Cloud Logs at any time. ## See also - [About logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [View logs using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) - [View logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) - [Tutorial | Generate logs to debug your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) - [Log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: View Logs using an External Monitoring Tool ## Article: Connecting Wix Logs to a Monitoring Tool Advanced.md ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md ## Article Content: # View Logs using an External Logging Tool You can view [logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) using an external monitoring tool of your choice. This is the general lifecycle of logs when connected to an external monitoring tool:
Wix logs lifecycle
1. Your site generates implicit and explicit logs in JSON format and sends them to the endpoint you provided. 2. The endpoint converts the JSON objects into a format that the external monitoring tool can parse. 3. The converted JSON objects are sent to the monitoring tool for analysis. ## Step 1: Create an endpoint You can create an endpoint by implementing your own or by using one provided by an external monitoring tool.
**Important:** - Your site sends logs in the form of JSON objects. Make sure to configure the endpoint to handle JSON objects so that the external monitoring tool can parse them correctly. - Your endpoint must be configured to accept the POST request method.
### Use an endpoint by an existing monitoring service You can use an endpoint provided by an existing monitoring service, such as [logz.io](https://logz.io/) or [Loggly](https://www.loggly.com/). Refer to the service's documentation to learn how to create an endpoint. ### Implement your own endpoint Alternatively, you can implement your own endpoint and integrate it with a monitoring tool of your choice. > **Note**: You can create an endpoint for logs on a Wix site using the [HTTP functions API](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/introduction.md), but you can't do so on the same site whose logs you want to view. Your endpoint must include code to convert [log objects](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) into a format that can be parsed by the external monitoring tool. For your convenience, Wix offers an open-source example using [Solarwinds Loggly on Google App Engine](https://github.com/wix/corvid-loggly-telemetry-adapter). The code is specific to this tool, but you can adapt it for the monitoring tool of your choice. ## Step 2: Set your endpoint Once you have an endpoint, set it in the site dashboard: 1. In the site dashboard, click **Developer Tools** > **Logging Tools**. 2. In the **Connect an external logging tool** section, click **Next**. The Connect external logging tool page appears.
3. In the **Enter your endpoint URL** field, enter your endpoint URL and click **Connect**. Your site is now connected to the external monitoring tool. ## See also - [About logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [View logs using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) - [View logs using Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [Tutorial | Generate logs to debug your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) - [/log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Log Object Structure ## Article: Site Log Object Structure ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md ## Article Content: # Log Object Structure The log object contains messages and metadata about events and errors that occur on your site. [Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) are generated by Wix, and with the exception of the payload message, their structure and data can't be modified. ## Object structure It is important to understand log object structure for effective debugging and monitoring. The log object includes the following fields: | Field | Type | Description | | --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `id` | string | Each log entry is assigned a unique ID. A log entry that has the same `id` as another log entry indicates a duplicate. | | `insertId` | string | Each log is assigned a unique ID. A log that has the same `insertId` as another log indicates a duplicate. | | `timestamp` | string | Time the log event occurred. Date string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date format. | | `receiveTimestamp` | string | Time the log was received by [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) or the [external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md). Date string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date format. | | `severity` | string | Severity of the log. Possible values:

`INFO`: Status, performance, or other general information.

`WARNING`: An unexpected or undesired development that may cause problems.

`ERROR`: A serious issue likely to cause problems.

`DEBUG`: Logs that are only useful while debugging and generally not relevant for production. | | `labels` | object | Additional information about the log. | | `labels.siteUrl` | string | Base URL of the site associated with the log. | | `labels.namespace` | string | Name of Wix app associated with the log, such as [Wix Stores](https://www.wix.com/app-market/wix-stores) or [Wix Bookings](https://www.wix.com/app-market/bookings).

The generic namespace `Velo` represents site code not associated with any specific Wix app. **Only the `Velo` namespace is supported. You currently can't enter the name of any other Wix app.** | | `labels.tenantId` | GUID | Unique site identifier. Does not change even if the site name or other site parameters change. | | `labels.viewMode` | string | Viewing mode in which the log originated. Possible values:

`Preview`: [Site preview mode](https://support.wix.com/en/article/studio-editor-saving-previewing-and-publishing-your-site#previewing-a-site).

`Site`: [Published site](https://support.wix.com/en/article/studio-editor-saving-previewing-and-publishing-your-site#publishing-a-site). | | `labels.revision` | string | Unique site revision identifier. You can use the `revision` property together with the [Release Manager](https://support.wix.com/en/article/about-site-monitoring#site-revisions) to monitor and debug new site revisions. | | `operation` | object | Additional information about a potentially [long-running operation](#long-running-site-operations) associated with the log. | | `operation.id` | string | Unique operation identifier. | | `operation.producer` | string | Operation context, such as the page or web module where the operation took place. | | `sourceLocation` | object | Source code location information. Applies to backend log entries with the `Velo` namespace. | | `sourceLocation.file` | string | File that generated the log. | | `sourceLocation.line` | number | Number of line that generated the log. | | `sourceLocation.column` | number | Number of the column on the line that generated the log. | | `jsonPayload` | object | Log payload in JSON format. | | `jsonPayload.message` | string | Payload message. | ### Long-running site operations Some operations, such as a method call or a [scheduled job](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md), generate multiple log entries. The `operation` object allows you to find the logs associated with a specific operation. Use the unique `operation.id` field to identify which operation generated the logs, and the `producer` property to view the context in which the operation took place. ## Example Here's an example log object: ```json { "insertId":"6axgiF6X45ftx7A1bjJBCh", "timestamp": "2019-03-31T06:02:12.008Z", "labels": { "siteUrl": "https://my-site.com", "revision": "12", "namespace": "Velo", "tenantId": "fbd4e3c6-8e55-4098-b0ff-5e478b6c957e", "viewMode": "Preview" }, "sourceLocation": { "file": "backend/backend.web.js", "line": 26, "column" : 16 }, "operation": { "id": "1554017373.14484058793726342", "producer": "backend" }, "jsonPayload": { "message": "Backend event triggered." }, "receiveTimestamp": "2025-01-20T06:02:12.925Z", "id": "455b7bef-8ba2-4914-9808-0f9f98084f71", "severity": "INFO" } ``` ## See also - [About Wix logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [View logs using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [View logs using Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [View logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) - [Tutorial | Generate a log to debug your site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Generate Logs to Debug Your Site ## Article: Generating a Log to Debug Your Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md ## Article Content: # Tutorial | Generate Logs to Debug a Site This tutorial demonstrates how to generate logs from site code. We will then view the generated logs using [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). In this tutorial, we will: 1. Add a button to a site's home page. 1. Create an event handler that generates a log whenever the button is clicked. 1. View the generated log using Wix Logs. > **Note**: In this tutorial, logs are generated from page code. However, you can also generate logs from backend code. ## Step 1 | Add a button to your site Add a button to your site and create an event handler: 1. Add a button labeled `button1` to your site's home page, like this:
1. In the site's home page code, add an [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) that runs whenever the button is clicked. For the purposes of this tutorial, let's produce an error by defining `factor3` instead of `factor2.` ```js $w("#button1").onClick(() => { const factor1 = 10; const factor3 = 5; try { console.log("Button clicked!"); console.log(factor1 + " * " + factor2 + " = " + (factor1 * factor2)); } catch (error) { console.error("An error occurred during multiplication:", error); console.warn("Make sure to specify 2 factors for multiplication."); } }); ``` 1. Preview or publish your site. > **Note:** You can generate logs when previewing your site, but some log information is available only when your site is published. ## Step 2 | Generate and view a log Generate a log and view it [using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) as follows: 1. In a separate browser tab or window, [open the Wix Logs page](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md).
1. In your live site's home page or in Preview mode, click the button you created. The logs appear in the Wix Logs page:
1. Click **View** for the log with the error to see its full details on the right:
Check out the `jsonPayload` to see an explanation for the error. ## See also - [About logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [View logs using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) - [View logs using Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [View logs using an external monitoring tool](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) - [Log object structure](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/log-object-structure.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Scheduling Recurring Jobs ## Article: Scheduling Recurring Jobs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md ## Article Content: # About Scheduling Recurring Jobs Scheduling jobs allows you to schedule code to run at specified intervals. If you have code you need to run on a recurring basis, you can automate the process and prevent the need for human intervention by scheduling a recurring job. You can create jobs to run on any backend function. For example, you might create jobs that: * Import or export data once a day. * Delete collection data that is no longer relevant once a week. * Send a status report to relevant parties once a month.   ## Supported IDEs You can schedule recurring jobs using: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## Times You can schedule jobs to run on an hourly, daily, weekly, or monthly basis. All times in jobs are scheduled according to [UTC time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). To calculate the UTC equivalent of your local time, find your location's UTC time offset. Then either add or subtract to your local time based on the offset. For example, the Eastern Time Zone has an offset of -5, meaning it's 5 hours behind UTC time. To convert Eastern Time to UTC, add 5 hours. The job runs within 5 minutes of the specified time. ## Limits The number and frequency of jobs you can schedule depends on whether you have a [free site](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/using-velo-with-a-free-wix-site.md) or a [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md): + **Free sites and most premium plans:** You can add up to 20 scheduled jobs that run at a minimum of 1 hour intervals. + **Elite and Business Elite premium plans:** You can add up to 30 jobs that run as frequently as once every 5 minutes. ## Config tool To simplify the process of building your `jobs.config` file, you can use this third-party [Jobs Config tool](https://shoonia.github.io/jobs.config). It allows you to enter the details of your jobs and builds the jobs object for you. The tool also includes a validator that can check your existing `jobs.config` file for errors. ## See also + [Schedule recurring jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/schedule-recurring-jobs.md) + [Jobs: JSON object](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/jobs-json-object.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Schedule Recurring Jobs ## Article: Schedule Recurring Jobs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/schedule-recurring-jobs.md ## Article Content: # Schedule Recurring Jobs You can schedule [recurring jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md) to run on any backend function at specified intervals. To schedule recurring jobs on your site: ## Step 1 | Add a backend function Add an exported function to the backend that you want to run with a scheduled job. The function can be in any backend `.js`, `.web.js`, or `.jsw` file. ## Step 2 | Add the jobs.config file in the backend The way that you add the `jobs.config` file depends on which IDE you're using. To add the `jobs.config` file: ### Editor 1. Go to the **Public & Backend** section of the code panel. 2. Hover over **Backend**, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/04083fa4-3262-4606-98d1-cbc359bf8585/1a26226e-c281-42d2-8a60-88e44fe40c61.png), and select **Add scheduled jobs**. A `jobs.config` file opens in the code editor. The file includes a sample JSON object for defining scheduled jobs. ### Wix IDE or your local IDE 1. Add a `jobs.config` file to the `src/backend` folder. 2. Copy the [sample JSON object](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/jobs-json-object.md#jobs-json-object) and paste it into the file. ## Step 3 | Edit the JSON object to define the scheduled job The `jobs.config` file contains a JSON object which defines all the scheduled jobs for your site. Each object in the `jobs` array contains the following properties: + `functionLocation` + `functionName` + `description` (optional) + `executionConfig` Use the [jobs JSON object reference](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/jobs-json-object.md) for additional details on each property.
Tip: To simplify the process of building your jobs.config file, you can use this third-party Jobs Config tool. It allows you to enter the details of your jobs and builds the jobs object for you. The tool also includes a validator that can check your existing jobs.config file for errors.
Here is an example of a JSON object configured to send a status report every Monday morning at 8:00 AM UTC time: ```json { "jobs": [ { "functionLocation": "/utils.web.js", "functionName": "sendStatusReport", "description": "Send a weekly status report.", "executionConfig": { "time": "08:00", "dayOfWeek": "Monday" } } ] } ``` ### Schedule multiple jobs Schedule multiple jobs in the `jobs.config` file by adding additional objects to the `jobs` array. ```json { "jobs": [ { "functionLocation": "/utils.js", "functionName": "sendStatusReport", "description": "Send a morning status report.", "executionConfig": { "cronExpression": "0 8 * * MON" } }, { "functionLocation": "/utils.js", "functionName": "cleanDb", "description": "Delete stale items from the DB on the first of the month.", "executionConfig": { "time": "01:00", "dateOfMonth": 1 } } ] } ``` ## Step 4 | Publish your site You must [publish your site](https://support.wix.com/en/article/wix-editor-publishing-your-site) to save changes to your scheduled jobs. ## See also + [About scheduling recurring jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md) + [Jobs: JSON object](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/jobs-json-object.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Jobs JSON Object ## Article: Jobs JSON Object ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/jobs-json-object.md ## Article Content: # Jobs: JSON Object When scheduling [recurring jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md) on your site, you define the jobs in the `jobs.config` file. The `jobs` object in the file is structured as follows: ```json { "jobs": [ { "functionLocation": "", "functionName": "", "description": "", "executionConfig": { "" } } ] } ``` | Property | Type | Required | | ------------------ | ---- | -------- | | [`functionLocation`](#functionlocation) | String | Yes | | [`functionName`](#functionname) | String | Yes | | [`description`](#description) | String | No | | [`executionConfig`](#executionconfig) | Object | Yes | Here is an example of a JSON object configured to send a status report every Monday morning at 8:00 AM UTC time: ```json { "jobs": [ { "functionLocation": "/utils.web.js", "functionName": "sendStatusReport", "description": "Send a weekly status report.", "executionConfig": { "time": "08:00", "dayOfWeek": "Monday" } } ] } ``` ## `functionLocation` Path to the backend file that contains the function you want to run at the scheduled time. The function location is a relative path within the **Backend** folder. The function can be in any backend `.js`, `.web.js`, or `.jsw` file. ## `functionName` Name of the function to run at the scheduled time. > **Note:** Make sure that you export the function you want the job scheduler to run on. ## `description` Optional description of the job. ## `executionConfig` Object that contains information about when the job should run, using either a cron expression or time configuration properties. > **Notes:** > + All job configuration times are based on [UTC time](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md#times). > + Jobs occurring more than once a day must be defined with a cron expression. > + If both methods below are added to a single `executionConfig` object, only the cron expression is used. ### `cronExpression` Use a [cron expression](https://en.wikipedia.org/wiki/Cron#cRON_expression) as a single property in the `executionConfig` object. For example, to run a job every day at 8:00 in the morning, use: ```json { "executionConfig": { "cronExpression": "0 8 * * *" } } ``` ### `time`, `dayOfWeek`, `dateOfMonth` Use multiple properties, `time`, `dayOfWeek`, `dateOfMonth`, to schedule the job. `time` is the only required property with this method. If a job includes both `dayOfWeek` and `dateOfMonth`, an error occurs and the job will not run. + `time` The time of day the job runs. The time is specified as [UTC time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) in `HH:MM` format. + `dayOfWeek` One of: `Sunday`, `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday`, or `Saturday` + `dateInMonth` Number between `1` and `31`. For example, to run a job every Sunday at 8:00 in the morning, use: ```json { "executionConfig": { "time": "08:00", "dayOfWeek": "Sunday" } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Triggered Emails ## Article: Getting Started with Triggered Emails ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md ## Article Content: # About Triggered Emails Triggered emails allow you to automatically send customized emails for specific site events. For example, you might trigger emails to send: + A coupon to customers on their birthday. + A satisfaction survey following an issued refund. + A “next steps” email after a form is submitted.
Tip: As an alternative to triggered emails, you can use Wix Automations to reach out to your visitors.
## Supported IDEs You can schedule recurring jobs using: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor). ## Monthly email marketing balance Every site has a quota of monthly emails available to send. You can check the current balance under **Monthly balance** in your site's [Email Marketing dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Email%20Marketing&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/shoutout/). Triggered emails count toward the [email marketing balance](https://support.wix.com/en/article/email-marketing-understanding-your-monthly-balance) but continue to send without interruption even once the quota is reached. You can also [upgrade your email marketing plan](https://www.wix.com/my-account/site-selector/?title=Select%20a%20Site&buttonText=Select%20Site&actionUrl=https%3A%2F%2Fwww.wix.com%2Fwix-emails-plans%3FmetaSiteId%3D%7B%7BmetaSiteId%7D%7D%26origin%3Dkb) for a larger sending quota. ## Statistics for triggered emails vs email marketing The [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) maintains [statistics](https://support.wix.com/en/article/viewing-your-email-marketing-statistics) related to the triggered emails you create. Triggered email statistics are based on all emails sent, including test emails you send to yourself. This is different from the statistics for email marketing campaigns in your [Email Marketing dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Email%20Marketing&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/shoutout/), which are based on emails sent to unique contacts. Therefore, the statistics for triggered emails may seem inflated compared to email marketing statistics. ## Site collaborators [Site collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site#inviting-people-to-collaborate-on-your-site) can create and manage triggered emails. To do so, you must [give them](https://www.wix.com/my-account/site-selector/?buttonText=Manage%20this%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7B%7BmetaSiteId%7D%7D/manage-website/authorization) both **Manage Triggered Emails** and **Manage Email Marketing Campaigns** [permissions](https://support.wix.com/en/article/roles-permissions-overview#roles-and-permissions). ## See also + [Set up a triggered email](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/set-up-a-triggered-email.md) + [Tutorial: Send a triggered email to contacts](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-contacts.md) + [Tutorial: Send a triggered email to members](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-members.md) + [Tutorial: Send emails using the SendGrid npm package](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Set Up a Triggered Email ## Article: Creating a Triggered Email ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/set-up-a-triggered-email.md ## Article Content: # Set Up a Triggered Email Set up a [triggered email](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md) to automatically send customized emails for specific events on your site. ## Step 1 | Navigate to the Triggered Emails dashboard page To access the Triggered Emails dashboard page, you can either: + Select **Developer Tools** from the code sidebar and click **Triggered Emails**. + Select **Developer Tools** from [your site’s dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) and select **Triggered Emails**. ## Step 2 | Create an email Use the editor to create and design a new email: 1. On your [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails), click **+ New Campaign** to create a new email, or click the **More Actions** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/149e0d5e-6f90-423d-9f3c-7342557186d1/2018/03/26/3b056efa-d2c8-4cc2-b5fc-1cdfe2770936.png) icon and select **Duplicate** to duplicate an existing triggered email. 2. Design your email in the editor. ## Step 3 (Optional) | Add variables to personalize text Add variables to personalize the text of your email. The variable is a placeholder that will be replaced by a value you define in the code. 1. With a text element selected in the editor, click **+ Add Variable**: ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fcaec601e7155d7ffa74cdcb27847d63.png) 2. Fill out the fields in the popup: + **Variable name**: Name the variable something meaningful so it’s easy to identify later. For example, `newSubscriberName`. + **Fallback value**: A replacement text that displays here in case you’re missing that specific piece of data. You can apply fallback values in the body of an email, but not in the subject line. For example, with the fallback value `there`, in a case where you don't have the new subscriber's name the email will fallback to "Hey there,". 3. Click **Add**. ## Step 4 | Test, save, or publish the email Once your email is designed you have the option at the top of the editor to preview, save, or publish the triggered email. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/de9b62255dd77d87f3eb37ee25727ff0.png) + Click **Preview & Test** to send yourself the email to see how it will appear in an inbox and to test any links you included in the email. + Click **Save as Draft** to save your email as a work-in-progress and returns you to the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails). Draft emails are not sent when the triggering event occurs. + Click **Save & Continue**. If this is the first time ever you're creating a campaign, you'll be asked to fill out [sender details](#step-5-–add-sender-details). That must be completed before publishing. ## Step 5 | Add sender details Sender details are required to send triggered emails. These details contain the information that appears as the sender’s name and email address when an email from your site is received: + **Sender’s name**: This is the name that displays in the recipient's inbox. + **Reply-to email**: This is the email address that will populate as the send-to address in your reader's email when they click Reply.
Important information about the email address included in reply-to email: + Emails sent with a reply-to email with a public domain, such as `@gmail.com` or `@yahoo.com`, tend to land in the spam or promotions category. It's better to use an email address with an authenticated domain, such as `you@yourbusiness.com`. + If you use a public domain address, Wix redirects your email through our own authenticated address `@wixemails.com` or, for free users, `@wixsitemail.com`. When someone replies to your email, their new email will automatically populate the send field with the reply-to email. + You may be asked to confirm your reply-to email. You'll receive an email in your inbox with those instructions.
Click **Save** to continue. ## Step 6 | Get the code snippet Before redirecting back to the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) you have the opportunity to make changes to the email ID and the audience who will receive the email: ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9c5d6d3da3f03171968688308d8cc1be.png) 1. Update the unique **Email ID** to one that’s more friendly to work with. For example, `newsletter_signup`. This email ID is used in the code snippet. 2. Click **Email new contacts** or **Email site members** to display the appropriate snippet. 3. Copy the code snippet now or access it later from the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails). 4. Click **Save**. ## Step 7 | Add the code snippet to an event on your site Take the code snippet you copied from the previous window or hover over a triggered email in the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) and click the **View code snippet** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb658cc2897e698b84136109a16fc45c.png) icon and add the snippet to your code. ## See also + [About triggered emails](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md) + [Tutorial: Send a triggered email to contacts](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-contacts.md) + [Tutorial: Send a triggered email to members](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-members.md) + [Tutorial: Send emails using the SendGrid npm package](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Sending a Triggered Email to Contacts ## Article: Sending a Triggered Email to Contacts ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-contacts.md ## Article Content: # Velo Tutorial: Sending a Triggered Email to Contacts [Triggered Emails](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md) allow you to create a template for emails that you can send to a newly created contact, using code. Before sending the email, your code can inject information into the template to personalize it with any data that is available in your page code. In this article, we demonstrate how to use the code snippet generated by Triggered Emails to send an email to the newly created contact on the submission of a form. Although this article uses a form submission for demonstration purposes, you can send an email from anywhere in your code. The general idea is to paste the generated snippet into your code where you want the email to be sent. Then edit the snippet so that it uses the ID of the newly created contact and the values you want to insert for the variable placeholders.
**Note:** For a more general-purpose article on sending an email using a 3rd party email service, see [How to Send an Email on Form Submission](https://support.wix.com/en/article/how-to-send-an-email-on-form-submission).
### Prerequisites This article assumes you are familiar with [creating an input form](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/processing-user-input-before-it-is-stored-in-a-collection-with-data-hooks.md). In this example we'll assume you've [created and published](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/set-up-a-triggered-email.md) the following Triggered Email:
Notice that the email template contains the following variables: * name * Interest\_Area It is good practice to give your email template a meaningful name. Doing so makes it easier to work with in code. Click on the Email ID to rename it. In this example, we call our email **newsletter\_signup**.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/15/d83bfefa-ea9e-4d27-809f-69c7aa559ed3.png)
To create a Triggered Email for Contacts, select the Email New Contacts tab. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/15/ea31d34d-dea2-4ced-baf9-5ae2b6dd1835.png)
The code snippet generated by the email looks like this: ```javascript import {triggeredEmails } from 'wix-crm-frontend'; // ... const options = { variables: { name: , interest_area: } } triggeredEmails.emailContact('newsletter_signup', , options); ``` There are a few things to note about the code in this snippet: * At the top of the snippet there is an `import` statement. This line needs to be added to the very top of the code on the page where you will be using the rest of the snippet. It imports the library of functions that lets your code work with the Triggered Email functionality. * The contact ID and the values of the variables (`name` and `interest_area`) are reflected in the code snippet with placeholders. These placeholder values will need to be replaced with real values in your actual code. ### Add a Custom Field to Your Contacts List In our example we save some information about our contact in a custom field. To work with custom fields in our code we first need to add the [custom field](https://support.wix.com/en/article/adding-custom-fields-to-contacts) to the Contact List in the Dashboard. For our example, name the field interest\_area. ### Form Next, we create an input form with a submit button. In this example, we use a simple form with the following input elements:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/15/97ff0ea4-716e-410c-8485-16e0187af529.png)
|Type |ID |Usage | |---|---|---| |Input - text|nameInput|For entering a name |Input - email|emailInput|For entering an email address |Dropdown|interestArea|For selecting area of interest |Button|signUpButton|For submitting the data |Dataset|newsletterDataset|For connecting the elements ### Code Finally, we write the code to send the Triggered Email described above when the form is successfully submitted. The code will send the values from the form to be used in place of the variables in the email template.  **Here's the outline of what we'll need to do:** 1. Add the import statement to the top of the code where we use the snippet. We will also need to import an additional module to create new site contacts. 2. Add an onClick event handler to the button. The code we add to that handler function will create the new contact and email them. 3. Add code that creates the new contact and gives us the contact's ID. 4. Add the snippet code to the handler function and replace the placeholder values. 5. Add some code to handle success and errors.
**Note:** Triggered Emails may not work properly when previewing your site. Publish your site to test the code found below.
**1\. Add the import statement to the top of the code where we use the snippet.** Paste the import statement that was at the top of your snippet: `import {triggeredEmails} 'wix-crm-frontend';` to the top of the code in the page where you want to use the snippet. Add `contacts` in between the `{ }` to import that module as well. The final import statement should look like this: import { triggeredEmails, contacts } from 'wix-crm-frontend'; **2.** **Add an onClick event handler to the button. The code we add to that handler function will create the new contact and email them.** Use the Properties & Events panel to add an `onClick()` event handler to the signUpButton that runs each time it's clicked. **3\. Add code that creates the new contact and gives us the contact's ID.** Add this code inside the onClick event handler. This uses the [appendOrCreateContact](https://www.wix.com/velo/reference/wix-crm-frontend/contacts/appendorcreatecontact) API to create the new contact using the information the site visitor entered in the form fields. ```javascript contacts.appendOrCreateContact({ name: { first: $w('#nameInput').value }, emails: [{ email: $w('#emailInput').value }], extendedFields: { interest_area: $w('#interestArea').value } }); ``` The `appendOrCreateContact` function returns an object containing the `contactID`, and `identityType` for the newly created contact. We'll grab the ID and use it in our snippet to identify the new contact and email them. **4\. Add the snippet code to the handler function and replace the placeholder values.** The `appendOrCreateContact` function returns a promise, so we'll add our snippet after it. We'll declare a new variable called `contactId` and use it to store the `contactId` value that is stored in the object returned by `appendOrCreateContact()`. We will use this ID in the snippet in place of ``. We'll also use the values that our site visitor entered in the form fields as the actual values for the `name` and `interest_area` variables in the `variables` object. At this point our code should look like this: ```javascript export function signUpButton_click(event) { contacts.appendOrCreateContact({ name: { first: $w('#nameInput').value }, emails: [{ email: $w('#emailInput').value }], extendedFields: { interest_area: $w('#interestArea').value } }) .then((contactInfo) => { const contactId = contactInfo.contactId; triggeredEmails.emailContact("newsletter_signup", contactId, { "variables": { "name": $w('#nameInput').value, "interest_area": $w("#interestArea").value } }) }); } ``` **5\. Add some code to handle success and errors.** Since the `emailContact()` function returns a Promise, we can define what happens when the Promise resolves successfully or rejects with an error. We'll add a `.then` and a `.catch` to handle these. Your code should look like this now: ```javascript import { triggeredEmails, contacts } from 'wix-crm-frontend'; export function signUpButton_click(event) { contacts.appendOrCreateContact({ name: { first: $w('#nameInput').value }, emails: [{ email: $w('#emailInput').value }], extendedFields: { interest_area: $w('#interestArea').value } }) .then((contactInfo) => { const contactId = contactInfo.contactId; triggeredEmails.emailContact("newsletter_signup", contactId, { "variables": { "name": $w('#nameInput').value, "interest_area": $w("#interestArea").value } }) .then(() => { // do something after the email was sent }) .catch((err) => { // handle the error if the email wasn't sent }); }); } ``` Test your form in your published site to see that it creates the contact and sends a Triggered Email. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Sending a Triggered Email to Members ## Article: Sending a Triggered Email to Members ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/sending-a-triggered-email-to-members.md ## Article Content: # Velo Tutorial: Sending a Triggered Email to Members
Visit the Velo by Wix website to onboard and continue learning.
[Triggered Emails](https://support.wix.com/en/article/about-triggered-emails) allow you to create a template for emails that you can send to the currently logged-in site member, using code. Before sending the email, your code can inject information into the template to personalize it with any data that is available in your page code. In this article, we demonstrate how to use the code snippet generated by Triggered Emails to send an email to the currently logged-in site member on the submission of a form. Although this article uses a form submission for demonstration purposes, you can send an email from anywhere in your code. The general idea is to paste the generated snippet into your code where you want the email to be sent. Then edit the snippet so that it uses the ID of the currently logged-in member and the values you want to insert for the variable placeholders.
**Note:** For a more general-purpose article on sending an email using a 3rd party email service, see [How to Send an Email on Form Submission](https://support.wix.com/en/article/how-to-send-an-email-on-form-submission).
### Prerequisites This article assumes you are familiar with [creating an input form](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/processing-user-input-before-it-is-stored-in-a-collection-with-data-hooks.md). In this example we'll assume you've [created and published](https://support.wix.com/en/article/creating-a-triggered-email) the following Triggered Email. You will also need a way for members to [log into your site](https://support.wix.com/en/article/adding-a-members-area-to-your-site).
Notice that the email template contains the following variables: * name * sport * comments It is good practice to give your email template a meaningful name. Doing so makes it easier to work with in code. In this example, we call our email **sportMail**.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/01/ff4f8c8e-0175-46ef-8532-51a2b4bc7c5c.png)
To create a Triggered Email for Contacts, select the Email Site Members tab. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/16/3e038a6f-0a8a-4f51-95e2-7c0ff465a248.png)
The code snippet generated by the email looks like this: ```javascript import {triggeredEmails } from 'wix-crm-frontend'; // ... const options = { variables: { name: , sport: , comments: } } triggeredEmails.emailContact('sportMail', , options); ``` The ID of the Triggered Email (`"sportMail"`) and the names of all the variables it contains (`name`, `sport`, and `comments`) are reflected in the code snippet. ### Form Next, we create an input form with a submit button. You can use an existing form or create a new one. Either way, this is a regular input form without any special setup. Each input element is connected to a field in a dataset and a button is connected to the same dataset with the **Submit** action.  In this example, we use a simple form with the following input elements:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/03/29/281e1980-b261-4afc-ad0f-cb0ea9de4878.png)
|Type |ID |Usage | |---|---|---| |Input|nameInput|For entering a name |Dropdown|sportDropdown|For entering a preferred sport |Text Box|commentsInput|For entering comments |Button|submitButton|For submitting the data |Dataset|sportDataset|For connecting the elements ### Code Finally, we write the code to send the Triggered Email described above when the form is successfully submitted. The code will send the values from the form to be used in place of the variables in the email template. 
**Note:** Triggered Emails may not work properly when previewing your site. Publish your site to test the code found below.
To do so, we register an `onAfterSave()` event handler that runs each time a new item is successfully submitted. Inside the handler function, we paste the code snippet generated when we created our Triggered Email. We also have to import the `triggeredEmails` module, since it contains the `emailMember()` function. At this point, our code looks like this: ```javascript import { triggeredEmails } from 'wix-crm-frontend'; $w.onReady(function () { $w("#sportDataset").onAfterSave( () => { triggeredEmails.emailMember("sportMail", , { variables: { "name": , "sport": , "comments": } } ); } ); } ); ``` However, it still won't work as intended.  We have to edit the snippet so that the code: * checks to see if a site visitor is logged in  * if the site visitor is a logged-in member, uses their ID to send the email * uses the values from the form that was submitted ```javascript import { triggeredEmails } from 'wix-crm-frontend'; import { currentMember } from 'wix-members-frontend'; $w.onReady(function () { $w("#sportDataset").onAfterSave(async () => { const member = await currentMember.getMember() if (member) { const userId = member._id; triggeredEmails.emailMember("sportMail", userId, { variables: { "name": $w("#nameInput").value, "sport": $w("#sportDropdown").value, "comments": $w("#commentsInput").value } }) } }); }); ``` Notice that we import the `currentMember` module from `wix-members-frontend`. This module gives us access to a member's details if they are logged in. import { currentMember } from 'wix-members-frontend'; We call the `getMember()` function which returns a Promise that resolves to a logged-in member's details: await currentMember.getMember() We check if the site visitor is logged in by seeing if `getMember()` returned anything: if (member) { If the site visitor is logged in we get their member ID: const memberId = member._id; And use the `memberId` variable in the `emailMember()` function call: triggeredEmails.emailMember("sportMail", memberId, { We also replaced the values in the `variables` object with the values from our input elements. "name": $w("#nameInput").value, "sport": $w("#sportDropdown").value, "comments": $w("#commentsInput").value We can also add code to verify that the email was sent and handle cases where an error has occurred. Since the `emailMember()` function returns a [Promise](https://support.wix.com/en/article/velo-working-with-promises), we can define what happens when the Promise resolves successfully or rejects with an error. ```javascript import { triggeredEmails } from 'wix-crm-frontend'; import { currentMember } from 'wix-members-frontend'; $w.onReady(function () { $w("#sportDataset").onAfterSave(async () => { const member = await currentMember.getMember() if (member) { const userId = member._id; triggeredEmails.emailMember("sportMail", userId, { variables: { "name": $w("#nameInput").value, "sport": $w("#sportDropdown").value, "comments": $w("#commentsInput").value } }) .then(() => { // do something after the email was sent successfully }) .catch((err) => { // handle error that prevented the email from being sent }); } }); }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About AI Assistants ## Article: About AI Assistants ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/about-ai-assistants.md ## Article Content: # About AI Assistants AI Assistants are Wix tools that allow you to generate code using prompts and built-in artificial intelligence (AI). Each assistant is designed for specific development environments and use cases. ## Wix AI Assistant in the Wix IDE The [Wix AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md) is a comprehensive coding companion for backend development. This assistant helps you: - Generate backend code based on your descriptions. - Debug and fix errors in your existing code. - Look up documentation and learn about Wix APIs. - Have interactive conversations with follow-up questions and clarifications. This assistant is ideal for complex backend logic, API integrations, and when you need detailed explanations of Wix development concepts. ## AI Code Assistant in the Wix Studio Code Panel The [AI Code Assistant in the Wix Studio Code Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md) specializes in frontend development. This assistant helps you: - Generate custom code that triggers when interacting with elements on a site. - Add the generated code directly to a site's page code. This assistant is perfect for adding interactive features to a site's elements. ## Choosing the right assistant - Use the assistant in the Wix IDE when you want assistance with backend functionality, complex logic, API integrations, or comprehensive debugging help. - Use the assistant in the Code panel when you want assistance with adding custom interactive behaviors to frontend elements. Both assistants are designed to accelerate your development process while helping you learn Wix development best practices. ## See also - [About the Wix AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md) - [About the AI Code Assistant in the Wix Studio Code Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md) - [Generate backend code using the Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/generate-code-using-the-wix-ai-assistant.md) - [Write event handlers with the AI Code Assistant in the Wix Studio Code Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/write-event-handlers-with-the-ai-code-assistant-in-the-wix-studio-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Wix AI Assistant ## Article: Wix Studio: Wix AI Assistant ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md ## Article Content: # About the Wix AI Assistant in the Wix IDE The Wix AI Assistant is an intelligent coding companion built into the [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide). It provides real-time assistance to help you write code, fix errors, discover features, and learn how to use Wix APIs to create sites. Use the Wix AI Assistant to: + **Generate code:** Write a code snippet for a site based on your description. + **Fix code:** Debug errors in your code. + **Look up documentation:** Inquire about new features and access detailed information from [Wix's API reference](https://dev.wix.com/docs/velo.md). ## Supported IDEs You can use the Wix AI Assistant in the [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide). ## Code snippets When the Wix AI Assistant provides a code snippet, you can insert it or copy and paste it directly into the Wix IDE.
![Code snippet](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5c1dffe0af4e9ef6641e8095b282c25f.png)
## Interactive chat The Wix AI Assistant provides an interactive chat interface, so you can continue your conversation with the assistant after receiving its initial response. Examples of furthering conversations are: - Ask clarifying questions. - Request corrections. - Make further queries that build on the previous one. You can ask up to 2 follow-up questions after each initial query. After this, the Wix AI Assistant starts over. ## See also - [About AI assistants](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/about-ai-assistants.md) - [Generate backend code using the Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/generate-code-using-the-wix-ai-assistant.md) - [About the AI Code Assistant in the Wix Studio Code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md), to generate frontend code for elements using prompts. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Generate Code Using the Wix AI Assistant ## Article: Generate Code Using the Wix AI Assistant ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/generate-code-using-the-wix-ai-assistant.md ## Article Content: # Generate Code Using the Wix AI Assistant Use the [Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md) to help you work on a site's code inside the [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide). 1. To access the Wix AI Assistant, click the **Wix AI Assistant** AI Assistant Icon icon in the Wix IDE sidebar or right click in the Wix IDE and select **Open the Wix AI Assistant**. The **Wix AI Assistant** panel opens.
2. Select the type of help you need. 3. In the text input field, describe [what you want the code to do](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), and then click send ![Send Button](https://github.com/wix-incubator/wix-code-docs/assets/66324550/11994058-d69d-41d3-9e5e-8c0fc74c8156).
Tip: To get immediate code fix suggestions, highlight the code and right click Wix AI Assistant: Fix code.
![code-snippet](https://github.com/wix-incubator/wix-code-docs/assets/66324550/e84b2b08-863f-407d-bea6-28d5ebfe87f0)
The code assistant creates a code snippet that performs the requested task and explains how the code works. 4. You can add the proposed solution to the code by selecting: - **Insert**: Inserts the code directly into the code file you are editing.
Note: If any code is highlighted in the Wix IDE, the inserted code replaces it. Otherwise, the code is inserted at the current cursor position.
- **Copy**: Copies the code snippet to paste it manually into the Wix IDE. 5. You can continue your conversation with the assistant after receiving its initial response. You can ask up to 2 follow-up questions after each initial query. After this, the Wix AI Assistant starts over. 6. After your chat is complete, click home home-icon at the top of the Wix AI Assistant panel to start over. > **Note:** Make sure to test the code in [preview mode](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) before publishing the site. ## See also - [About AI assistants](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/about-ai-assistants.md) - [About the AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), to generate backend code using prompts. - [Studio Editor: Using the AI Code Assistant to Add Custom Code](https://support.wix.com/en/article/studio-editor-using-the-ai-assistant-to-add-custom-code) - [About the AI Code Assistant in the Wix Studio Code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the AI Code Assistant ## Article: Writing Event Handlers with the AI Code Assistant ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md ## Article Content: # About the AI Code Assistant in the Wix Studio Code Panel The [AI Code Assistant in the Code panel](https://support.wix.com/en/article/studio-editor-using-the-ai-assistant-to-add-custom-code) of the Studio editor uses a series of prompts to generate custom [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/write-event-handlers-with-the-ai-code-assistant-in-the-wix-studio-editor.md) code for elements on a site. The assistant also returns an explanation of the code it generates. With the AI Code Assistant, you can : - Disable a button for certain site visitors based on their location using browser locale and IP geolocation detection. - Register a hover event that changes an element's color when the mouse pointer hovers over it. ## Supported IDEs You can use the AI Code Assistant in the [Wix Studio Code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md). ## Before you begin Please note the following before using the AI Code Assistant in the Wix Studio Code panel: - The assistant in the Code panel only supports simple use cases that can be implemented in frontend [page code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md#page-code) files. It doesn't currently support use cases that require both frontend and backend code. To use an AI assistant for backend code, see [About the AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md). - The assistant doesn't support requests that require interactions with 3rd-party services, external APIs, or the use of npm packages. - The assistant currently only supports requests in English. ## See also - [About AI assistants](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/about-ai-assistants.md) - [Write event handlers with the AI Code Assistant in the Wix Studio Code Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/write-event-handlers-with-the-ai-code-assistant-in-the-wix-studio-editor.md) - [Studio Editor: Using the AI Code Assistant to Add Custom Code](https://support.wix.com/en/article/studio-editor-using-the-ai-assistant-to-add-custom-code) - [About the AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), to generate backend code using prompts. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Write Event Handlers with the AI Code Assistant in the Wix Studio Editor ## Article: Write Event Handlers with the AI Assistant in the Wix Studio Editor ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/write-event-handlers-with-the-ai-code-assistant-in-the-wix-studio-editor.md ## Article Content: # Write Event Handlers with the AI Code Assistant in the Wix Studio Code Panel > **Note:** This feature is only available to Wix Studio users. Use the [AI Code Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md) to write custom code behavior for elements on a site. To write an event handler with the **AI Code Assistant**: 1. Start a chat with the AI Code Assistant: + **On the canvas:** Right-click the element you want to customize and select **Add Custom Behavior with AI**. + **Through the Code panel:** Access the Code panel on the left side of the editor and select **AI Code Assistant**. 2. Select one of the assistant's suggestions or describe what you want to customize. The AI Code Assistant generates the code. 3. Select one of the following options: + **Show code** displays the generated code along with an explanation of the code. You can copy and paste this code into a site's [page code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md#page-code), or ask the assistant to make adjustments. + **Add code** directly adds the code into a site's [page code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md#page-code). ## See also - [About AI assistants](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/about-ai-assistants.md) - [About the AI Code Assistant in the Wix Studio Code Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-code-panel/about-the-ai-code-assistant.md) - [Studio Editor: Using the AI Code Assistant to Add Custom Code](https://support.wix.com/en/article/studio-editor-using-the-ai-assistant-to-add-custom-code) - [About the AI Assistant in the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), to generate backend code using prompts. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About n8n Integration ## Article: About n8n Integration ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/about-n8n-integration.md ## Article Content: # About n8n Integration [n8n](https://n8n.io/) is a workflow automation platform that lets you connect apps and services to create automated workflows. With the Wix n8n integration, you can automate tasks involving your Wix site without writing code, connecting Wix to hundreds of other services available in the n8n ecosystem. A key capability of n8n is its built-in AI agent. By combining Wix triggers with an AI agent, you can create workflows that analyze incoming data, determine appropriate actions, and execute them automatically. Before getting started, [connect n8n to your Wix site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/connect-n8n-to-your-wix-site.md). ## What you can do with the integration The Wix n8n integration provides two types of nodes, both powered by [Wix APIs](https://dev.wix.com/docs/api-reference.md): - Wix trigger node: Starts workflows automatically when an event occurs on your site. [Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/triggers/about-triggers?apiView=SDK.md) powers the triggers and provides events across Wix business solutions, such as form submissions, new orders, booking confirmations, contact updates, and membership sign-ups. - Wix node: Performs actions on your Wix site, such as managing products and orders in your Wix Store. You can create, update, delete, and query data as part of your n8n workflows. When configuring a Wix node, you can specify parameter values manually, map them from the trigger output, or use an AI agent to generate them dynamically based on the workflow context. To see the full list of available triggers and actions, browse the options in the Wix nodes' configuration panels in n8n. Learn more about [n8n nodes](https://docs.n8n.io/workflows/components/nodes/).
**Important:** The Wix nodes are currently community nodes. Before you can use them, you must install the `@wix/n8n-nodes-wix` package. See the n8n guide on [installing community nodes](https://docs.n8n.io/integrations/community-nodes/installation/).
## AI-powered workflows The built-in AI agent in n8n is a key advantage of using n8n with Wix. Instead of building complex conditional logic, you can use an AI agent to interpret trigger data and decide what actions to take. The agent can analyze incoming information, determine the appropriate response, and generate the parameters needed for any action in your workflow, whether that's a Wix node, an email service, a CRM, or any other n8n integration. For example, you can create a workflow that recovers abandoned checkouts. When a checkout is abandoned on your site, n8n triggers the workflow and an AI agent analyzes the details and generates the parameters for a discounted recovery order and a personalized email. The workflow then creates the order and sends the email to win back the customer. To expand the capabilities of your AI agent beyond the actions available in the Wix node, you can connect the [Wix MCP server](https://dev.wix.com/docs/api-reference/articles/wix-mcp/about-the-wix-mcp.md) as a tool. Connecting the Wix MCP gives your AI agent access to additional Wix APIs. ## Authentication To connect n8n to your Wix site, you need an [API key](https://dev.wix.com/docs/api-reference/articles/authentication/about-api-keys.md) and the site ID of the site you want to connect. These are added as credentials in n8n. To learn how to set up Wix authentication in n8n, see [Connect n8n to Your Wix Site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/connect-n8n-to-your-wix-site.md). ## Use cases Here are some examples of what you can automate with the Wix n8n integration: - Abandoned checkout recovery: When a checkout is abandoned, an AI agent analyzes the details and generates parameters for a discounted recovery order and personalized email. The workflow then creates the order and sends the email. - Smart lead routing: When a form is submitted, an AI agent evaluates the submission and categorizes the lead. The workflow then routes the lead to the appropriate team member or CRM. - Automated content moderation: When a new blog comment or forum post is published, an AI agent analyzes the content and decides whether to approve, flag, or remove it. The workflow then performs the appropriate action. - Dynamic customer support: When a chat message is received, an AI agent analyzes the query, retrieves relevant order or booking information, and generates a response. The workflow then sends the response to the customer. - Cross-platform sync: When product data changes in your Wix Store, automatically update inventory in external systems like spreadsheets or software solutions like Enterprise Resource Planning (ERP). ## n8n marketplace You can search the [n8n workflows](https://n8n.io/workflows/) for workflow templates built by others that use Wix nodes. If you're a developer looking to extend the Wix n8n integration or create your own Wix-related nodes, you can build and publish them to the marketplace for other Wix users to benefit from. ## See also - [n8n Docs](https://docs.n8n.io/) - [Connect n8n to Your Wix Site](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/connect-n8n-to-your-wix-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Connect n8n to your Wix Site ## Article: Connect n8n to your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/connect-n8n-to-your-wix-site.md ## Article Content: # Connect n8n to Your Wix Site Connect your Wix site to n8n to automate workflows using Wix triggers and actions. For an overview of the Wix n8n integration and what you can do with it, see [About n8n Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/about-n8n-integration.md). This guide walks you through creating API credentials and configuring n8n to work with your site. After completing this task, you can create workflows that react to events on your Wix site, perform Wix actions, connect to other n8n integrations, and use AI agents to power your automations.
**Important:** Wix n8n nodes are currently community nodes. Before you can use them, you must install the `@wix/n8n-nodes-wix` package. See the n8n guide on [installing community nodes](https://docs.n8n.io/integrations/community-nodes/installation/).
## Step 1 | Create an API key in Wix Create an [API key](https://dev.wix.com/docs/api-reference/articles/authentication/about-api-keys.md) in your Wix account with the permissions needed for the triggers and actions you want to use. To create an API key: 1. Go to the [API Keys Manager](https://manage.wix.com/account/api-keys) in your Wix account. 2. Click **Create API Key**. 3. Enter a name for your API key, such as "n8n Integration". 4. Select the permissions for the APIs you want to access. The permissions you need depend on which triggers and actions you plan to use. For example: - For product operations, select **Wix Stores** permissions. - For order operations, select **Wix eCommerce** permissions. 5. Click **Create**. 6. Copy and securely store your API key. You can't view it again.
__Important:__ Store your API key securely and never expose it in frontend code.
## Step 2 | Find your site ID Locate the ID of the Wix site you want to connect to n8n. To find your site ID: 1. Go to your [site's dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fhome). 2. Check the URL in your browser. The site ID appears after `/dashboard/` in the URL. For example, in `https://manage.wix.com/dashboard/12345678-1234-1234-1234-123456789012/home`, the site ID is `12345678-1234-1234-1234-123456789012`. Alternatively, you can use the [Query Sites](https://dev.wix.com/docs/api-reference/account-level/sites/sites/query-sites?apiView=SDK.md) API to retrieve all site IDs associated with your account. ## Step 3 | Configure Wix credentials in n8n Add your Wix API credentials to n8n to authenticate your workflows. To configure credentials: 1. In n8n, go to **Settings > Credentials**. 2. Click **Add Credential**. 3. Search for and select **Wix API**. 4. Enter your credentials: - **API Key**: Paste the API key you created in Step 1. - **Site ID**: Paste the site ID you found in Step 2. 5. Click **Save**. ## Step 4 | Test the connection Verify that your credentials are working by creating a simple workflow. To test the connection: 1. Create a new workflow in n8n. 2. Add a Wix node to the canvas. 3. When configuring the node, select your saved Wix API credentials. 4. Browse the available resources and operations to see what actions you can perform. 5. Select an operation and click **Test step** to execute it. If the connection is successful, you see results from your Wix site. ## See also - [About n8n Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/integrations/about-n8n-integration.md) - [About API Keys](https://dev.wix.com/docs/rest/articles/authentication/about-api-keys.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Debugging Your Code ## Article: Testing and Troubleshooting Your Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md ## Article Content: # About Debugging Your Code There are several ways you can debug the code in your Wix site: - Debugging with the Developer Console - Debugging with functional testing in the backend - Debugging with your browser's developer tools - Debugging with Wix Logs ## Debugging with the Developer Console The Wix Developer Console is available at the bottom of the page when you are previewing your site. The console displays information that is useful when debugging, such as errors, warnings, and other messages. It also displays debug messages that you added to the code using any [console method](https://developer.mozilla.org/en-US/docs/Web/API/console). If you are new to debugging code, click [here](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) to learn how to log messages to the console. Each message displays the page where the relevant code can be found and a link to the specific line of code (in the editor) that triggered the message. ### Viewing the Developer Console To view the Developer Console, open your site in Preview mode. The Developer Console appears at the bottom of the page. ![Developer Console](https://wixmp-833713b177cebf373f611808.wixmp.com/images/61a21a6485d5f527d42fc5417df978f5.png) If the console is minimized, click the arrow ![developer console arrow](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c27735328334e1589ef91c4f0dfb4aa7.png) to expand it. ### Clearing messages from the console Click ![clear console button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c79b7d7b6b7b7229338ba32f083e6fdf.png) on the console menu bar. ### Filtering console messages Click **Default View** on the console menu bar, and then choose which kinds of messages you want to see. ![Default View menu](https://wixmp-833713b177cebf373f611808.wixmp.com/images/003d8881d8517049e8b5430f866be849.png) + **Verbose**: System log messages that can help you debug low-level code problems. + **Debug**: Messages you have logged to the console. + **Info**: Informational messages that require no action. + **Warning**: Messages about potential problems in your code. These are highlighted in yellow. + **Error**: Messages about actual errors in your code. These are highlighted in red. ## Debugging with functional testing in the backend Debugging code from the backend is challenging because you need to invest effort to trigger the code and test it. To save you time and effort, Wix Studio and Wix Editor enable [quick testing of backend functions](https://support.wix.com/en/article/velo-testing-your-backend-functions) directly from the code editor. The output you see in the test tab is the same as the output you would see in the Developer Console if you previewed your site and triggered the function. You can debug HTTP functions by adding `console.log()` calls to them. The information you log appears in the function output when using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) (Wix Editor) and in your site's [Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md). ## Debugging with your browser's developer tools Wix allows you to [debug](https://developer.chrome.com/docs/devtools/javascript/) your site's code as you would any modern JavaScript web-based application, by using [developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools) to set breakpoints, log to the console, and more. These tools are not part of Wix - they come with your browser. If you are new to debugging, learn how to [log messages to the console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md). You can open a copy of your site's frontend code files in the browser's developer tools. For detailed instructions, read [Locate and Open Frontend Source Files](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/locate-and-open-frontend-source-files.md) You can debug your fully-published site's code directly, or without publishing using Preview mode. Read more about [About Testing Your Code in Preview Mode](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-testing-your-code-in-preview-mode.md). ### Expected behaviors when debugging in Preview mode Consider the following expected behaviors when debugging certain features in Preview mode: - **Backend code** Because of security concerns, messages in backend code might not be logged to the browser's console on the published version of your site. - **HTTP functions** Because HTTP functions are not browser-based, there is no console to display your logs. You can use [Wix Logs](#debugging-with-wix-logs) to view console messages in backend code and HTTP functions on your site. ### Source maps When you debug your code in the browser it looks just like your original source code. In reality, the code you write is not the actual code that is run. The usage of source maps means that you don't need to worry about what's going on behind the scenes. But if you're curious to know, this is what's happening.
Read more about source maps Wix supports writing code using the ES2017 standard. But until this standard is fully implemented in all browsers, your code is transpiled from ES2017 code to ES5. Your code is also minified, and source files are combined to make their delivery from the server more efficient. So the code that's actually being run on your site is transpiled, minified, and combined. You don't want to have to debug that generated code. You want to debug the code you wrote originally. That's where source maps come in.  A source map is a file that maps the lines of code that the browser is actually running to the lines in your original source code. When you want to debug some code, the source map lets you find the code in the original version that you wrote, even though the debugger uses the source map to run the corresponding generated code.  All this should happen without you having to do anything. If you’re having trouble, check your browser’s developer tools settings and make sure that source maps are enabled. 
## Debugging with Wix Logs [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) allows you to debug your site by generating and tracking logs in real-time by adding [console messages](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) to any code in your site. This includes frontend code, backend code, public code, HTTP functions, or anywhere else in your site. Logs are generated when in Preview mode and when working on a published site. To access Wix Logs while previewing, click **Logging Tools** and then **Wix Logs** in your [Developer Console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md). ![Logging tools button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5226fd913de0195684564357453b3541.png) Here is sample log: ![Logs](https://wixmp-833713b177cebf373f611808.wixmp.com/images/36338a0bd9887dabd3c1cd24d45d328b.png) Learn more about working with [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md#about-wix-logs). > **Note**: For more robust log analysis, you can connect to [Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) and other [external monitoring tools](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md). Learn more about: - [Viewing logs in your Wix Logs window](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) - [Connecting to Google Cloud Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-google-cloud-logs.md) - [How to generate a log using Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/generate-logs-to-debug-your-site.md) - [Wix Logs' logging details](https://www.wix.com/velo/reference/spis/site-monitoring/logentry) ## Debugging FORBIDDEN (403) errors Some functions require specific roles and permissions to run. Other functions may limit functionality depending on the site visitor's roles and permissions. If you are receiving a 403 or FORBIDDEN error when you run a function, try using the Wix Auth API's [`elevate()`](https://www.wix.com/velo/reference/wix-auth/elevate) function. This function allows you to bypass permission requirements by elevating the site visitor's permissions to the highest level. Exercise caution when using this function to prevent security vulnerabilities. ## See also - [About Testing Your Code in Preview Mode](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-testing-your-code-in-preview-mode.md) - [Locate and Open Frontend Source Files](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/locate-and-open-frontend-source-files.md) - [About Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Locate and Open Frontend Source Files ## Article: Locate and Open Frontend Source Files ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/locate-and-open-frontend-source-files.md ## Article Content: # Locate and Open Frontend Source Files When debugging your site's frontend code, you may want to view a copy of the code files in your browser's developer tools. To locate and open the files: 1. Identify the names of the files that contain your site's frontend code. The names of these files appear in the Wix Developer Console when you preview your site They also appear in the browser developer console. ![Developer console filename](https://wixmp-833713b177cebf373f611808.wixmp.com/images/70041265fd4a1dfe29c1bc998fbc7b18.png) 1. Browse to your site (published or in [Preview mode](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-testing-your-code-in-preview-mode.md)) and open your browser's developer tools. 1. Open the file search bar. - In Chrome press **command/Ctrl+O**. - In Firefox, open the **Debugger** panel in the developer tools and press **command/Ctrl+P**. 1. Type the name of your code file and select it from the search results to open it. ![Select code file](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8163fd532bdaae8cdaeed150b555535b.png) ## See also - [About Debugging Your Code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) - [About Testing Your Code in Preview Mode](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-testing-your-code-in-preview-mode.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Testing Your Code in Preview Mode ## Article: About Testing Your Code in Preview Mode ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-testing-your-code-in-preview-mode.md ## Article Content: # About Testing Your Code in Preview Mode You can test the code on your site before publishing using Preview mode. You can perform any actions in Preview mode that you would be able to perform on your live site. When testing your site, you can check if any errors appear in the [Wix Developer Console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md#debugging-with-the-developer-console). ## Supported IDEs Preview mode is available in Wix Studio and Wix Editor. If you're developing in the [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) or the [Local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md), you can open your site in Wix Studio or Wix Editor to use Preview mode. ## Enter Preview mode To enter Preview mode in **Wix Studio**, either click the **Run** button in the code editor toolbar, or the preview button on the top right. ![Open Preview mode in Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ffbb8a64bf8ec23768ffae14d7c60cc1.png) To enter Preview mode in **Wix Editor**, either click on the **Run** button in the [code editor toolbar](https://support.wix.com/en/article/velo-working-with-the-code-panel-toolbar-and-right-click-menu), or the **Preview** button in the Editor on the top right. ![Open Preview mode in Editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/07a171db7d78c7d40a3980c98fccd4d5.png) ## Collections in Preview mode When you've finished previewing and return to the Editor, interactions you had with your site aren't maintained. Your site returns to the state it was in before you entered Preview mode. The only exception is your optional [sandbox collection](https://support.wix.com/en/article/cms-about-sandbox-and-live-collections-and-syncing), which is disabled by default. The sandbox collection is useful when you want to test your site without affecting your site's data. When the sandbox collection is enabled and you interact with data from your collections, you'll be using the optional sandbox version of your collections, not the live version. Changes to data that result from interactions with your site in Preview mode persists in your optional sandbox database. ## Differences between Preview mode and published sites Although your can view your pages and interact with your site and data in Preview mode, the site doesn't behave exactly as it does when published: + When previewing your site, you are assigned the Admin [role](https://support.wix.com/en/article/about-database-collection-permissions#roles). That means you are granted all possible [permissions](https://support.wix.com/en/article/database-permissions#permissions). Users of your live site might have different permissions and therefore have more restricted access. + When previewing your site with [sandbox enabled](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing), you're working with the data in your site's optional [sandbox database](https://support.wix.com/en/article/sandbox-data-vs-live-data#sandbox-data). Users of your live site work with your [live database](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/importing-and-exporting-collection-data-with-code.md#using-the-form-with-the-live-collection-without-publishing-your-site). [Wix app collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md), however, only have the live database version. Any changes you make to these collections in Preview mode are reflected in your published site.   + Backend events and several other APIs don't necessarily work when previewing your site. We note when this is the case in the reference. > > You can try out a test version of your published site on a small percentage of visitors using the [Release Manager](https://dev.wix.com/docs/velo/articles/getting-started/testing-and-debugging.md#release-manager). This is helpful for testing functionality that only works on a published site without exposing that functionality to all site visitors. ## Handling errors If you see errors or your site isn't running as expected, there are tools you can use to debug the code in your site. + [Developer console:](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) Debug in Preview mode. + [Functional testing:](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) Trigger and debug backend code in the code editor. + [Browser developer tools:](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) Debug your sites in Preview mode, published sites, and test sites. + [Logs:](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md#debugging-with-wix-logs) Debug in Preview or Published mode. ## See also + [About Debugging Your Code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Functional Testing ## Article: About Functional Testing in the Backend ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md ## Article Content: # About Functional Testing Functional testing allows you to test your backend functions individually, making it easier to find and resolve bugs. The code editor comes with a quick-test environment for functional testing, which speeds up the development process by reducing the need to manually trigger and test your backend code by [calling functions from the frontend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md). ## Supported IDEs You can test your backend functions with functional testing using the editor (Wix Studio and Wix Editor). ## Functional testing tab You can access functional testing from the following code files in your site's backend: - .js files - .web.js files - .jsw files (deprecated) >**Note:** Functional testing only works for functions that are exported. The functional testing environment opens as a new tab in the code editor for each function you test. To test your functions, add sample input parameter values as a JSON object on the left. When you run the function, the output is displayed on the right side, where the call statement always appears. If there are no errors, the return statement and console logs generated by the function are displayed. If there is a problem with your code or the input parameter values, an error message will appear instead. This setup helps you easily debug and improve your functions by providing clear feedback in the form of call statements, return values, console logs, or error messages.
## How functional testing compares to preview mode The output you see in the functional testing tab is the same as the output you would see in the [Developer Console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/testing-and-troubleshooting-your-code.md#debugging-with-the-developer-console) if you previewed your site and triggered the function. Testing backend functions in the code editor has other similarities to testing in Preview mode: - When you test a function that affects a Wix app, the Wix app data gets updated. For example, a new product is displayed in your dashboard after testing a backend function that creates a new product. - When you test code that interacts with a database collection not associated with a Wix app, data is updated in your [sandbox collection](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/importing-and-exporting-collection-data-with-code.md) and not your [live collection](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/importing-and-exporting-collection-data-with-code.md). - If there is a problem with your code, you'll see the same errors you would see in Preview mode. - You can use [Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-wix-logs.md) to monitor your function output. The [view mode](https://www.wix.com/corvid/reference/spis/site-monitoring/logentry/labels) for Logs is "Preview".  ## See also - [Test Backend Functions with Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) - [Functional Testing Examples](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/functional-testing-examples.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Test Backend Functions with Functional Testing ## Article: Functional Testing in the Backend ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md ## Article Content: # Test Backend Functions with Functional Testing The code editor comes with a functional testing tab where you can test your backend functions individually. This reduces the need to manually trigger and test your backend code by [calling functions from the frontend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md), speeding up the development and debugging process. ## Test a backend function To test a single backend function with functional testing: 1. In the code panel, open the backend file that contains the function you want to test. 1. In the code editor, click the **play icon** button ![play icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1ac7cb9c0770f069c5fb697f77f69cfe.png) located to the left of your function.
This opens the functional testing tab. Your function name appears in the tab header. 1. In the Set Parameters section of the test tab, each parameter appears as a separate property in a JSON. Add [sample arguments](#LINK-TO-ADVANCED-FUNCTIONAL-TESTING-ARTICLE) for each property in the JSON.
>**Note** > If your function doesn't take any parameters, leave the JSON empty. 1. Click **Run** in the Set Parameters section.
The output appears on the right.
>**Note:** > If you close the functional testing tab, the last sample input parameter values and the most recent function output are saved and appear when you reopen the tab unless you refresh the page. ## Additional functional testing tab actions You can also perform the following actions in the functional testing tab: - **Clear sample parameter values:** Hover over your parameter values in the **Set Parameters** section and click the reset button ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/12/19/14d12f7e-38c4-42e8-92e4-f1f81c2b0e41/8b857a02-2741-4363-af44-d3092e547395.png). - **Clear test output:** Hover over your output in the View Function Output section and click the clear button: ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/12/19/5f1daab5-e507-4211-b1aa-c7364dbe00c8/8b3e2ccd-a0b9-4bce-8a94-cd4257dad2e9.png). ## See also - [About Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) - [Functional Testing Examples](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/functional-testing-examples.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Functional Testing Examples ## Article: Functional Testing Examples ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/functional-testing-examples.md ## Article Content: # Functional Testing Examples With functional testing you can easily test your backend functions individually, reducing the need to build your own system for triggering and testing your backend code. You can use functional testing to test a variety of scenarios, such as functions with: - [HTTP requests](#http-requests) - [Destructured parameters](#destructured-parameters) - [Missing arguments](#missing-arguments) - [JavaScript arguments objects](#javascript-arguments-objects) - [Date objects](#date-objects) ## HTTP requests Here is an example of a function with an HTTP request that performs basic arithmetic operations based on the request path and query parameters:
To test [HTTP functions](https://www.wix.com/velo/reference/wix-http-functions) with functional testing, specify the HTTP request object in the Set Parameters section. A request template is initially created for you with placeholder data, but you need to edit it to fit your specific needs. For example, when testing a `post` function, replace the placeholder JSON object in the `body` property with a JSON object you want to test with. The following properties are included in the request templates: * For `get` and `delete` functions: `path`, `headers`, and `query` * For `put` and `post` functions: `path`, `headers`, and `body` * For `options` functions: `path` and `headers` * For `use` functions: `path`, `headers`, `query`, and `method`
## Destructured parameters Destructuring allows you to extract values from arrays or properties from objects into distinct variables. Here is an example of a function that destructures the parameters `{ factor1, factor2 }` directly from the passed object and returns their product:
To test a function with destructured parameters, place parameter values in a JSON object inside an array:
## Missing arguments When you set default values in your function declarations, you can test those functions without needing to provide all of the arguments. Here is an example of a function that uses default values for `factor2` and `factor3`.
To test a function without providing all of the arguments, place the arguments in an array in the Set Parameters section, and leave out at least one of the arguments that has a default value. For example, the `multiplyWithDefaults()` function above was tested three times with the following arguments: * [1,2,3] * [1,2] * [1] The following results were returned: * 1 * 2 * 3 = 6 * 1 * 2 * 2 = 4 * 1 * -1 * 2 = -2
## JavaScript 'arguments' objects The JavaScript [arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) object can accept any number of arguments in a function. Here is an example of a function that uses the arguments object to find the sum of all the arguments provided.
To test a function containing the JavaScript [arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) object, place the arguments in an array in the Set Parameters section:
## Date objects Here is an example of a function that takes a Date object as the parameter:
To test a function with a parameter of type **Date**, you need to use a [stringified date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON) in the JSON object. You can use one of the following syntaxes: ```javascript { "date": { "$date": "" } } ``` ```javascript [ { "$date": "" } ] ```
## See also - [About Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) - [Test Backend Functions with Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Test Site Considerations ## Article: Test Sites and Velo ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/test-sites/test-site-considerations.md ## Article Content: # Test Site Considerations When using [test sites](https://support.wix.com/en/article/about-test-sites) on a site with code, it is important to understand how the different versions of your site work with the following features. ## Database collections When you have both a published revision and a test site revision of your site, you still only have 1 version of your site's database collections that both revisions access. That means, when you perform a data operation such as updating an item from either your published or test site revision, the change affects both of your site revisions. While your data does not change based on the site revision, your database [schema](https://support.wix.com/en/article/about-database-collection-schemas) may differ from revision to revision. This means that a field that exists in the schema in the test site revision may not exist in the schema in the published revision. Because collection data itself is maintained regardless of whether a field is in the schema, this should not affect the functionality of your site revisions. ## Backend events Currently your site must be published at least once for the version of the event handler in your test site to run when a [backend event](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) on your test site is triggered. For example, let's say you have a `onNewOrder` event that handles new events in your store. In your published site your event handler sends an email to `p@supplier.com` for each order received, but in your test site the same event handler sends an email to `rc@supplier.com`. Whenever a site visitor places an order on your test site, an email is sent to `rc@supplier.com`. If you define a backend event handler in your test site revision without ever publishing the site, messages in backend code are not logged to the browser's console and the event handler never runs. Keep in mind that messages in backend code are not logged to the browser's console. ## Scheduled jobs When you [schedule jobs](https://dev.wix.com/docs/velo/articles/getting-started/schedule-jobs.md) in your site's backend, only the jobs scheduled in your published site revision run. The jobs scheduled in your test site revision do not run. ## Service plugins To use [service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) in your test site revision, make sure to publish your site once after implementing the plugin. After that first initial publish, the service plugin implementation works on your test site revision. If the site hasn't been published, the published revision's implementation runs. Neither the published revision nor the test site revision runs your latest service plugin code. ## The Realtime API Currently the [Realtime API](https://dev.wix.com/docs/velo/api-reference/wix-realtime-backend/introduction.md) is partially supported for test sites. Keep the following considerations in mind: - [realtime_check_permissions](https://dev.wix.com/docs/velo/api-reference/wix-realtime-backend/realtime-check-permission.md) and [PermissionsRouter](https://dev.wix.com/docs/velo/api-reference/wix-realtime-backend/permissions-router/introduction.md) are not supported on test site revisions. - When publishing/subscribing to messages on a channel in both test site and published revisions, both sites receive messages from both versions. This means that the published site receives the messages from both the published site's backend and the test site's backend. Similarly, the test site receives the messages from the test site's backend and the published site's backend. It isn't possible to add new message types on the same channel with the aim to test it only in the test site revision because the published revision also receives the new message. This might cause unexpected behavior. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Site History ## Article: Working with Site History ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/site-history/working-with-site-history.md ## Article Content: # About Site History You can [manage the site history](https://support.wix.com/en/article/managing-your-site-history-1079227) of sites that include code in the same way you manage sites without code. You can revert the site to a previous version.
__Tip:__ Name your versions so you can easily find the version to revert to later.
This article explains why it is important to understand the implications of this restore. ## Supported IDEs You can revert the site to a previous version using: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor) + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio) + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor) > **Note**: When working in a local IDE, you can connect your site to Github for more advanced version control. ## Restored elements When you revert to an older version of your site, the following elements are reverted to their previous states: - Pages - Code - Hooks - Routers - Schemas - Permissions ## Non-restored elements Keep in mind that data in both the Sandbox and Live databases don't revert to the previous state. This means that the data remain as in the current version and you can't recover deleted data by restoring a previous site. ## Schema changes after reverting When you restore to a previous version, the schema follows the version being restored. This means if you made changes in fields with the newer version, those changes might not exist in the older version's schema. You can add fields to the older version's schema as needed. The data is retained and becomes visible once the fields are added. ## See also - [Manage Site History](https://support.wix.com/en/article/managing-your-site-history-1079227) - [About Database Collections](https://support.wix.com/en/article/about-database-collections) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with the Monitoring Dashboard ## Article: Working with the Monitoring Dashboard ## Article Link: https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/monitoring-your-published-site/working-with-the-monitoring-dashboard.md ## Article Content: # Velo: Working with the Monitoring Dashboard The [Monitoring Dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2F/developer-tools/monitoring-dashboard) helps you analyze a site's performance for backend code and data operations, and track its CMS collections. Use it to improve site visitors' experience by monitoring request performance, identifying issues, and optimizing site code. The Monitoring Dashboard has the following tabs: - **Data requests**: Tracks calls to a site's CMS collections. - **Backend requests**: Tracks backend code performance and errors. - **CMS collection storage**: Tracks a site's current storage usage relative to its quotas.
__Tip:__ Check the dashboard regularly to catch issues early. Investigate unusual increases in requests or failures, and methods with high p95 durations.
## Data requests The **Data requests** tab displays information about calls to a site's CMS collections. Every time you access your data, whether to read or write it, you make a data request. The **Data requests** tab includes the following sections: - **Overview**: Displays the number of successful and unsuccessful data requests, along with the error rate. When viewing the default 7-day period, the dashboard compares the error rate to the previous 7 days. - **All data requests**: Displays a graph of all requests made during the selected time period. Successful requests appear in blue and failed requests appear in red. - **Data requests by operation**: Breaks down request information by collection and operation type. Each row represents a unique pairing. The table includes the following columns: | Column | Description | | --- | --- | | Collection | The collection name. If the collection doesn't exist or multiple collections share the same name, the collection ID appears. | | Operation | The type of operation performed, such as **find**, **insert**, or **update**. | | Count | The number of times this operation ran on this collection during the time period. | | Error rate | The percentage of failed requests. | | Cache rate | The percentage of requests served from cache. | | Duration p50 | The median request duration in milliseconds. | | Duration p95 | The 95th percentile request duration in milliseconds, representing slower requests. | Click a row to navigate to a page with more details about requests for that specific collection and operation pairing. To minimize failed data requests: - Simplify complex queries. - [Add proper indexes to collections](https://support.wix.com/en/article/cms-adding-an-index-for-your-collection). - Use [efficient query patterns](https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-improving-performance-in-wix-sites-with-data.md). - Implement caching for frequently accessed data. - Consider pagination for large result sets. ## Backend requests > **Note:** The **Backend Requests** tab is still under development. To minimize failed backend requests: **For timeout issues:** - Reduce the number of operations in your method. - Use caching to avoid repeated calculations. - Optimize database queries with proper filtering and sorting. **For RPM throttling:** - Implement request batching. - Add delays between rapid requests. - Consider upgrading your [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md) for higher limits. ## CMS collection storage Each Wix site can have a maximum of 1,000 collections and 10 million collection items, which can take up no more than 100 GB of storage space. The **CMS collection storage** tab lets you know how close a site is to these quotas, and lists CMS collections that Wix users created on the site. These don't count toward the storage space quota: - External database collections. - Wix app collections. - Media files stored separately on a site. - Referenced documents and multimedia. ## See also - [About Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) - [Data features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) - [Compute features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) - [About caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [Best practices for building a site](https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-building-a-site-with-velo.md) - [Best practices for improving site performance](https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-improving-performance-in-wix-sites-with-data.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Where Do I Put My Code? ## Article: Where Do I Put My Code? ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md ## Article Content: # About Code Placement When developing websites, it’s important to understand where to place your code so it runs in the right context, whether that’s in the browser, on the server, or across all pages. This article gives an overview of the different types of code files available, and explains when and how to use each one. ## Supported IDEs You can store your code using the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) ## Frontend code Frontend code runs in the site visitor’s browser and here you define the interactive parts of your site. This includes things like: - Responding to user input - Updating the UI - Making calls to backend services Since this code is exposed to the public, avoid including sensitive information, such as API keys or private logic. Use frontend code for: - Customizing how a page looks or behaves - Listening for events like button clicks or form submissions - Calling functions defined in backend web modules (`.web.js`) ### Running code on all pages with Global code Global code is code that runs on every page of your site. This is logic that's shared across all pages, such as managing a universal header or footer, or controlling global UI elements like a site-wide search bar or shopping cart icon. To add global code, edit the `masterPage.js` file. You'll find it in: - Wix Studio: The **Page Code** section of the **Code** panel - Wix Editor: The **Page Code** section of the **Code** sidebar - Local IDE: The `src/pages` folder when using [Git integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli/.md)
**Important:** `masterPage.js` has its own `onReady` event handler. Be careful not to duplicate logic from individual page `onReady` handlers, as both run in parallel.
#### Retaining an element's state You can also use global code to retain an element’s state across pages. For example, if you change the source of an image element that appears on all pages, that change won’t persist when navigating to another page, or even when returning to the original page. To make changes persistent across pages, use the [`wix-storage-frontend`](https://www.wix.com/velo/reference/wix-storage) API: 1. In the relevant event handler, call [`setItem`](https://www.wix.com/velo/reference/wix-storage/storage/setitem) to store a value that reflects the element’s new state. 1. In the `masterPage.js` `onReady` function, read the stored value and update the element accordingly. > **Note:** Avoid importing functions from `masterPage.js` into individual page files. Doing this causes the `onReady` function in `masterPage.js` to run twice on those pages. For shared logic, create code on a separate public file and import it where needed. ### Running code on a specific page Each page on your site has its own code file. Use these files to add functionality that's specific to that page, for example: - Handling user input - Customizing UI behavior - Calling backend functions. #### Public files If you find yourself repeating the same code across multiple page files, it's better to move shared logic to a separate **public** file. That way, you can write it once and import it wherever it's needed: Whether in a page file, a backend file, or another public file. To share a function across multiple files: 1. Create a public file and export the function from it. 1. Import the function into any page where it’s needed. You can find your public files in: - Wix Studio: The **Backend & Public** section of the **Code** panel - Wix Editor: The **Backend & Public** section of the **Code** sidebar - Local IDE: The `src/public` folder when using [Git integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli/.md) Use ES module syntax (`import`/`export`) to access functions between files. Always include the function name in curly braces when importing a named export: ```js import { myFunction } from 'backend/bookings.js' ``` Leaving out the curly braces attempts to import a default export: ```js import myFunction from 'backend/bookings.js' ``` Unless `myFunction` was explicitly exported as the default, this will throw a runtime error: ```js (0 , \_bookings.default) is not a function ``` To access all functions from a module, import the entire module without curly braces: ```js import wixData from 'wix-data'; import wixStoresFrontend from 'wix-stores-frontend'; import wixMembers from 'wix-members-backend'; ```
**Tip:** Your page code, which is also publicly accessible, doesn't appear in the list of public files. To see your page code, go to the **Page Code** section and click on the page in the menu.
### Responding to element actions with event handlers Page elements like buttons, text, and input fields can trigger events when site visitors interact with them. The functions that handle these events are called event handlers. There are following types of event handlers: - Dynamic event handlers. Learn how to add an event handler in [Add an Event Handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/add-an-event-handler.md). - Static event handlers (deprecated). Learn more about static event handlers in [About Static Event Handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-static-event-handlers.md). ### HTML Adding raw HTML directly in the code editor isn't supported. That said, there are several ways to include or work with HTML on your site: - Add marketing code using [marketing integrations](https://support.wix.com/en/marketing-tools/marketing-integrations-tracking). - Set the [HTML property](https://www.wix.com/velo/reference/$w/text/html) of a text element to render HTML content. - Display external HTML content by [embedding a site element](https://support.wix.com/en/article/wix-editor-using-iframes-to-display-visible-content-on-your-site). - Insert [custom code snippets](https://support.wix.com/en/article/embedding-custom-code-to-your-site) into the head or body of your site pages. - Use [custom elements](https://support.wix.com/en/article/wix-editor-adding-a-custom-element-to-your-site) to define and embed your own HTML-based components. ## Backend code Backend code runs on Wix’s secure servers and isn’t exposed to site visitors. This is the place for logic that needs to be protected, like: - Interacting with sensitive data - Sending emails - Handling payments - Verifying permissions Use backend code for: - Perform secure database operations - Respond to frontend requests by using web modules - Handle server-side events and scheduled tasks - Expose custom HTTP methods to other services ### Backend files Backend files are where you define server-side logic. They include code that runs securely on the web server rather than in the browser. This is where you define functions from backend libraries such as `wix-pay-backend`, `wix-members.v2`, or `wix-events.v2`. You can find your backend files in: - Wix Studio: The **Backend & Public** section of the **Code** panel - Wix Editor: The **Backend & Public** section of the **Code** sidebar - Local IDE: The `src/backend` folder when using [Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli/.md) There are 2 main file types used in the backend: - **.web.js:** Web modules that can be called from the frontend. You can configure permissions for each function to control who can access them. Learn more about the .web.js files in [About Web Modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md). - **.js:** Internal backend files that can't be accessed directly from the frontend. Use these for secure server-side logic. > **Note:** You may also see `.jsw` files in older projects. These have been deprecated in favor of `.web.js` files but are still supported. Learn more about the .jsw files in [Call Backend Code from a jsw Web Module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-a-jsw-web-module.md). #### Accessing functions from a .js file To access functions from a `.js` file in the frontend, import the function into a `.web.js` file first, then import that `.web.js` function into your frontend code. Import the `.js` file into the `.web.js` file: ```js import { Permissions, webMethod } from 'wix-web-module'; import { deleteStaffMember } from 'backend/bookings' export const deleteStaff = webMethod( Permissions.Anyone, async (staffMemberid) => { return await deleteStaffMember(staffMemberid); } ); ``` Import the `.web.js` file in your page file: ```js import { deleteStaff } from 'backend/staff.web' ``` If you try to import a `.js` file directly into a frontend file, you'll see this error: ``` Access to backend script 'backend/calculations.js' denied! Client-side scripts can only import web-modules from backend code context. ``` ### Backend events Many Wix modules provide built-in events that trigger when specific actions occur. For example, uploading a file to the Media Manager triggers the `onFileUploaded` event, and successfully paying an invoice triggers the `onInvoicePaid` event. These events let you run custom logic in response to key moments in your site’s workflow. To run an event, you need to add an `events.js` file. Learn more about adding backend events in [About Backend Events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md). ### Handling HTTP requests To expose your site’s functionality to the outside world, like enabling `GET`, `PUT`, `POST`, or `DELETE` requests from external services, you can define custom HTTP methods. To create an HTTP function, you need to add an `http-functions.js` file. Learn more about the HTTP functions in [About Custom Site APIs](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md). ### Using data hooks when data collection changes Data hooks let you run code before or after changes are made to your site’s data collections. They’re useful for validating, modifying, or responding to data operations like insert, update, or remove. To create data hooks, add a `data.js` file to your backend code. You can then define functions that correspond to specific data operations on your collections. Learn more about data hooks in [Using Data Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md). ### Routers By default, Wix uses built-in routers to handle dynamic page URLs and SEO. If you need more control over how incoming requests are handled, you can create your own custom router to define the logic, routing, and data for each request. The code for both routers and data binding router hooks is defined in the `routers.js` file. Learn more about routers in [About Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md). ## Secrets Manager For sensitive information like API keys, OAuth tokens, or configuration settings, use the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) instead of hardcoding values into your code. Secrets are stored securely in your [site's dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2F/developer-tools/secrets-manager) and can be retrieved in code using the [Secrets API](https://dev.wix.com/docs/velo/apis/wix-secrets-backend-v2/introduction.md). Storing secrets in the Secrets Manager improves your site's security, especially when you're collaborating with others or using version control. Each secret has a unique name, and you can access it safely in backend code without exposing it to site visitors. ## See also - [About Event Handlers in Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) - [About the Site Backend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/about-the-site-backend.md) - [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Velo Glossary ## Article: Velo Glossary ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md ## Article Content: # Velo Glossary [Let us know](mailto:velo.docs@wix.com?subject=Glossary%20feedback) if we missed any terms you would find helpful.
__Tip:__ See the full [Velo API reference](https://www.wix.com/velo/reference/api-overview) to start coding on your Wix site.
## $w $w is the [syntax](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md#velo-syntax-and-autocomplete), or set of rules, that you use in your code to select an element on your page. To select an element, type $w in the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio), or the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) (Wix Editor), then enclose the name of the element in parentheses and quotes, and add a hashtag before the element name. For example, $w('#myButton'). Once you select an element, you can use the [APIs](https://support.wix.com/en/article/velo-glossary#api) to work with its properties and functions in your code. ## API [Velo APIs](https://dev.wix.com/docs/velo/articles/api-overview/introduction.md) are sets of properties and functions that let you use code to control your site's functionality. There are [APIs](https://www.wix.com/velo/reference/api-overview/introduction) that let you interact with page elements, your site’s database content, and external services. The APIs also give you access to information about your site and its visitors. ## Backend The Backend section is located in the Public & Backend section of the Code sidebar (Wix Studio), or the Code sidebar (Wix Editor). This is where you add code that you want to run server-side. Backend files and code are not publicly accessible from your site. You may want some of your code to run server-side and not in the front-end; for example, your site might be exposed to security risks if the code runs in the front-end. You can call server-side code from your front-end code using [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md), and even test your backend code in the editor using [Functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md). ## Blocks [Wix Blocks](https://support.wix.com/en/article/creating-a-velo-package-3964385) allows you to build your own [Velo packages](https://support.wix.com/en/article/velo-about-packages#velo-packages), and reuse your code across multiple Wix sites. ## Calculated fields When you create a dynamic page, Velo calculates what the URL would be for any items that could appear on that page. The [prefix](https://support.wix.com/en/article/url-prefixes-and-page-grouping) and dynamic sections of that URL are added to each item in your collection as a [calculated field](https://support.wix.com/en/article/about-calculated-fields). The dynamic page you create can display any item whose calculated field is the same as the [prefix and dynamic sections](https://support.wix.com/en/article/cms-about-dynamic-pages) you define when you create the page. ## Code packages [Code packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) are pre-written code, also known as code libraries that you can add to your site(s). Using code packages is a great way to minimize your development time and the amount of code you need to write. There are 2 types of code packages: [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) and [npm Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md). You can add these packages in the Code Packages section of the Code sidebar. You can also create your own Velo Package using Wix [Blocks](https://support.wix.com/en/article/creating-a-velo-package-3964385). ## Code editor You edit the code for your site in the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio), or in the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) (Wix Editor), located at the bottom of the editor. The code editor displays your site's code files in tabs. When you use the Properties and Events panel to add an event to your element, the code for that event is automatically placed in the tab for that page's code. If you have code that relates to a specific page only, you would add it here. When an element appears on all your site pages and you want to add functionality to it that will be consistent across your site, add that code in the **masterPage.js** file in the Page Code section of the Code sidebar (Wix Studio), or the Code sidebar (Wix Editor). When you use the Properties panel to add an event to your element that appears on all pages, the code for that event is automatically placed there. If you have code that relates to all the pages of your site, add it here. ## Connect to data panel To work with [connecting to data](https://support.wix.com/en/article/connecting-page-elements-to-data), you need to connect elements on your page to a [dataset](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) using the Connect to Data panel. To open the panel, click the Connect to Data icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2017/06/27/639025d8-ab7a-4c52-8a3a-519ab5e5ca12.jpg) for your element.  ## Connecting to data [Connecting to Data](https://support.wix.com/en/article/connecting-page-elements-to-data) is the process of connecting your elements to a [collection](#database-collection). You can bind your elements to a collection to display content or to capture user input. To bind your elements to a collection you need to have a [dataset](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) on your page. ## CMS The Content Management System (CMS) is where you edit the contents of your [database collections](https://support.wix.com/en/article/managing-your-content-collections). There is a CMS in the Editor for your [Sandbox Database](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) and in the [Wix Database app](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) for your Live Database. Sandbox and Live [are not always enabled](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing) in the CMS. ## Database A database is made up of one or more [collections](#database-collection). You can add as many collections as you need to your database to manage your site's content. Your site has two databases that are separate but related to each other. One database is your site's [Sandbox database](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md), and the other is your site's [Live database](#live-database). The Sandbox database is where you work while developing your site. The Live database is what your visitors interact with when your site is published. You add a collection to your database in the Editor, and when you publish your site its live version is added to the Live database. You view the Live database in the [Wix Database App](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md). Sandbox and Live are not always enabled for your collections. Learn more [here](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Database collection A [database collection](https://support.wix.com/en/article/about-database-collections) is a table of data that you can use in a Wix site. Each row in the table represents an [item](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) in the collection. Each column in the table is a [field](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) in your collection, which can be of a specific data type. You can connect elements in your site to fields in your collection using [connecting to data](https://support.wix.com/en/article/connecting-page-elements-to-data). ## Dataset A [dataset](https://support.wix.com/en/article/about-datasets-6368396) is a component that you add to your page that enables you to connect the elements on your page to your [collections](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md). A dataset controls which collection is available for your elements to use, whether your elements can display, add to, or modify the items in your collection, as well as how the data is sorted or filtered. You can use a dataset on a regular page as well as a dynamic page. Datasets are not displayed in your published site. ## Dataset mode The [dataset mode](https://support.wix.com/en/article/working-with-dataset-modes-and-collection-permissions) is a setting that controls what the elements that are connected to your collection can do with the collection's data. Options are Read & Write, Read-only, and Write-only.  ## Dynamic item page A [dynamic item page](https://support.wix.com/en/article/cms-setting-up-a-dynamic-item-page) is a [dynamic page](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) that is used to display one item from your [collection](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) at a time. When you create the [URL](https://support.wix.com/en/article/cms-creating-unique-dynamic-page-urls) for a dynamic item page, you need to use a field in your collection that uniquely identifies each item in your collection. ## Dynamic list page A [dynamic list page](https://support.wix.com/en/article/cms-setting-up-a-dynamic-list-page) is a [dynamic page](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) that is used to display a number of items from your [collection](#database-collection) that all match the same criteria. When you create the [URL](https://support.wix.com/en/article/cms-creating-unique-dynamic-page-urls) for a dynamic list page, you need to use fields in your collection that identify a group of items in your collection. ## Dynamic page A [dynamic page](https://support.wix.com/en/article/cms-about-dynamic-pages) is a page you design with one layout that can be used over and over again, each time displaying a different item or items from your database collection. The actual content a dynamic page displays is determined by its [URL](https://support.wix.com/en/article/cms-creating-unique-dynamic-page-urls). There are two types of dynamic pages, [dynamic item pages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) and [dynamic list pages](#dynamic-list-page). ## Dynamic page dataset A [dynamic page dataset](https://support.wix.com/en/article/about-data-binding-and-datasets#about-dynamic-page-datasets) is a component that is added to your page automatically when you create a dynamic page. Like a regular [dataset](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md), it enables you to connect the elements on your page to your collections. It also allows you to add more filters and to sort the content on your page.  ## Element ID The element ID is the name assigned to an element in your site. When you hover over or select an element in the Wix Editor, you can see the element’s ID following the hashtag. Velo assigns a default ID to every element you add to your site. You can change the ID in the [Properties panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md).  The ID is used to identify the element in your code. For example, to work with an element named myButton, you would write $w(“#myButton”) in your code. ## Event An [event](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) is something that can happen to an element, usually as the result of a user action. For example, onClick is the event that occurs when a user clicks an element. If you want your site to do something, or react, when an event occurs to your element, you add an [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) to your element.  ## Event handler An [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) is the function that holds the code you want to run when an [event](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) occurs to an element. Your site watches elements to see if events happen to them. If an event happens that has an event handler, the code in the event handler function will run. ## Field A [field](https://support.wix.com/en/article/about-database-collections#regular-fields) is the place in your [collection](#database-collection) where you store data, and is represented as a column in the CMS. Every item in your collection is made up of one or more fields that can be of type Text, Image, Boolean, Number, Date and Time, or Rich Text. Collections also have default [system fields](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) that are hidden by default.  ## Field ID A [field ID](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields#field-id-velo-by-wix-only) is the name that you use to refer a field in a collection, in code. When you add a new field in the CMS, you can specify the field ID. If you don't specify your own field ID, one is created based on the Field Name. You cannot change the field ID once the field has been created. ## Field name The [Field Name](https://support.wix.com/en/article/about-database-collections#field-name) is the label you see at the top of the column for that field in the CMS. The Field Name is also used when connecting page elements to a dataset in the Editor. When you add a new field in the CMS, you specify the Field Name. You can change the Field Name after the field has been created, and all connections to that field will be updated.  ## Field type The [Field Type](https://support.wix.com/en/article/about-database-collections#field-type) defines what kinds of data the field contains. The field type can be Text, Image, Boolean, Number, Date and Time, Rich Text, URL, or Document. When you add a new field in the CMS, you specify the field type.  The field type is used when connecting page elements to fields in your collections. ## Functional testing With [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md), you can easily and quickly [test and debug your backend code directly in the Velo code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md). Functional Testing saves you the time and effort of building your own system for triggering and testing your backend functions.  ## Hook A hook is code that runs before or after certain interactions with your site. [Data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) run code before or after certain interactions with your site's collections. [Data binding router hooks](https://support.wix.com/en/article/about-data-binding-router-hooks) allow you to intercept the process of a dynamic page's data getting bound to the page. Some data binding router hooks can be used with [router pages](https://support.wix.com/en/article/custom-routers) as well. ## HTTP Endpoint The URL where a web service can be accessed by an application. In Velo, web services built on HTTP endpoints are used to connect [logging tools](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) and [external database collections](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/adding-and-deleting-an-external-database-collection.md) to Wix sites.  ## HTTP Functions [HTTP functions](https://support.wix.com/en/article/about-compute-functions) let you create functions to expose the functionality of your site as a service. That means other people can use the functionality of your site by writing code that calls your site's API as defined by Velo Functions you create. ## Interaction An interaction is custom functionality you add that allows your site to respond to [events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) that occur in your site.  ## Item An [item](https://support.wix.com/en/article/about-your-database-collection-fields) is one record in a [collection](#database-collection) and is represented as a row in the CMS. Every item in your collection is made up of one or more [fields](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md). ## Live database The [Live database](https://support.wix.com/en/article/managing-your-sandbox-and-live-data#live-data) is the database that is associated with your published site. The data in the Live database is what is visible on your published site when you display data. It is also the database where data you collect in your live site is stored. You can [sync](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) between databases and copy data from the Sandbox to your Live database, or overwrite your Live data to your Sandbox to work with it in the Editor. Sandbox and Live are not always enabled for your collections. Learn more [here](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Log Collection of [log entries](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) associated with the same Wix site and namespace. Each log has a [labels](https://www.wix.com/velo/reference/spis/site-monitoring/logentry/labels) section, which indicates the log’s site URL and namespace. ## Log Entry [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) displays logs as a single [log entry](https://www.wix.com/velo/reference/spis/site-monitoring/logentry) in JSON format. You can [view log entries](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) in real time as they’re recorded, or [connect them](https://support.wix.com/en/article/connecting-site-events-to-a-monitoring-tool) to an external monitoring tool for analysis. ## Logs [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) lets you gather information about logs, HTTP functions, and web module functions. You can [view](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) logs in real time, or [connect](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-an-external-monitoring-tool.md) logs to an external monitoring tool to generate event metrics and perform error log analysis. Each log is represented by a [JSON object](https://www.wix.com/velo/reference/spis/site-monitoring/logentry). ## Monitoring Dashboard Velo's built-in [Monitoring Dashboard](https://support.wix.com/en/article/velo-working-with-the-monitoring-dashboard) is a tool for monitoring site performance. You can access the Monitoring Dashboard in the Developer Tools section of the Code sidebar. It’s important to track your code’s performance on sites containing backend code and [Wix Data](https://www.wix.com/velo/reference/wix-data) API requests. You can also use [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) to monitor the site’s performance. ## onReady When a page loads in a browser, it's possible for the code on the page to run before the page finishes loading. This can cause an error if your code tries to reference an element in the page before it's loaded. Because of this, you need to make sure that all the elements on your page have loaded before you try to access them using code. You do this is by including all your code that uses the [$w selector](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) in the function that is added by default to the top of every page's code: ```javascript $w.onReady(function() { // Put all your $w code here }); ``` ## Payload Payload refers to the actual data sent in a message. In [Wix Logs](https://www.wix.com/velo/reference/spis/site-monitoring/logentry), the [JSON payload](https://www.wix.com/velo/reference/spis/site-monitoring/logentry/jsonpayload) is the message included in the site activity’s [log](https://www.wix.com/velo/reference/spis/site-monitoring/logentry), such as an error message or console log. ## Permissions [Permissions](https://support.wix.com/en/article/about-database-collection-permissions) give you control over which visitors are allowed to interact with the data in your collections and what they are allowed to do. The available permissions are create, read, update, and delete. You can assign different permissions to different [roles](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md).  ## Primary field A collection's Primary field is used to enable another collection to connect to it and [reference](https://support.wix.com/en/article/cms-using-reference-fields-to-display-content-from-multiple-collections) its data.  Every database collection has a Primary field. The Primary field is indicated by the lock icon next to its field name. By default, the Title field is the Primary field. However, you can define any text field in the collection as the Primary field.  ## Promise A JavaScript function that returns a promise is returning a contract to eventually produce a return value when an [asynchronous operation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous) is completed. A Promise is said to be fulfilled or resolved when its value is finally known. There are two main ways to [work with promises](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) that are returned from a function: * **then( )** - Allows you more control, but is a bit more complicated. * **async/await** - Allows you to work with asynchronous code as if it were synchronous. ## Properties panel The [Properties panel](https://support.wix.com/en/article/working-with-the-properties-panel-6441151) lets you work with Velo on the elements in your site. You can use the Properties panel to add [event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) to your elements and define their default behavior when your page loads. ## Public/front-end The Public section of the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#built-in-code-panel) Code Files section contains files that are publicly accessible from your site. The code you add here runs in the front-end, or client.  ## Query A query is a request for information from a database collection. For example, you might want to retrieve a list of all of the site members in your Members collection who live in a specific country. You can query your collections using the [wix-data query](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md) function. ## Regular editor The remote Wix Editor you use to edit and preview your site's UI and code. ## Release manager In the [Release Manager](https://support.wix.com/en/article/accessing-the-release-manager) you can see the current published version of your site and the current [test site](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md). You can also create, edit, and delete test site versions. ## Remote repository This is where all your site revisions are stored online. When you perform a save in the Editor, a new site revision is added to your [Site History](https://support.wix.com/en/article/viewing-and-managing-your-site-history) in the remote repository. ## Repeaters Repeaters allow you to create one layout for a group of elements. The repeater then uses that layout for each Repeater Item it displays. Each Repeater Item uses the same layout but displays different content. You can use Repeaters to display static content, meaning you set the elements' content for each Repeater Item in the Editor and it doesn't change. You can also connect Repeaters to the [data in a collection](https://support.wix.com/en/article/displaying-database-content-in-a-repeater). Each Repeater Item then uses the same element layout to display different content in your collection, based on how you connect your elements. ## Revision Every time you manually save or publish your site following a change, a new revision of your site is created. You can view and manage your revisions in your [Site History](https://support.wix.com/en/article/viewing-and-managing-your-site-history). You can also create a [test site](https://support.wix.com/en/article/creating-a-test-site) or [monitor Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) for a specific revision of your site. ## Roles Together with [permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md), [roles](https://support.wix.com/en/article/about-database-collection-permissions#roles) give you control over which visitors are allowed to interact with the data in your collections and what they are allowed to do. Every person that visits your site falls into one of four roles, and you can assign specific permissions to each role. Possible roles are admin, site member author, site member, and anyone.  ## Router A [router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) lets you customize exactly how your site handles incoming requests. You can customize what happens when your site receives a request to display a page. You can also control the information that search engines use to index the pages in your site. ## Sandbox database The [Sandbox database](https://support.wix.com/en/article/managing-your-sandbox-and-live-data#sandbox-data-1) is the database that is associated with your site in the Wix Editor. Data in the Sandbox is not visible on your published site unless you [sync](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) your collection with your [Live database](#live-database). Data in the Sandbox is affected by your actions in Preview mode. Sandbox and Live are not always enabled for your collections. Learn more [here](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Schema/structure A [database schema](https://support.wix.com/en/article/about-database-collection-schemas) is the definition of a database's structure. In Velo, the schema defines which fields are in each of your collections and their [type](https://support.wix.com/en/article/about-database-collections#field-type). However, in some cases, your collections can contain fields that are not part of the schema. When you create a new field in the CMS in the [Sandbox database](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md), the field is added to the schema. The schema is synced from your Sandbox collections to your Live collections when you publish your site. [Syncing](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) a collection's data will not sync the collection's schema. ## Scope A scope defines which elements you can select with a selector function, such as [$w( )](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md), and how those elements behave when they are selected. Depending on which context you received a selector function from, it will either select from the [global scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md) or the [repeated item scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md). ## Service plugin Velo [service plugins (formerly SPIs)](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) allow you to extend and customize your site’s functionality. You can use [service plugins](https://www.wix.com/velo/reference/spis/getting-started) to inject your own custom logic into a part of an existing app’s flow. You can also use service plugins to integrate with 3rd-party services so that your Wix site and the external services can communicate seamlessly. ## Sidebar The [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md) in Wix Editor, or [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#built-in-code-panel) in Wix Studio, shows all of the files that make up your site, including pages, popups, files, routers, and database collections. Working in the sidebar, you can perform a variety of actions that affect your site. ## Submit button A [Submit button](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/processing-user-input-before-it-is-stored-in-a-collection-with-data-hooks.md) is a button that you add to a page with User Input elements that enable visitors to submit data they have entered, to your collection. You create a Submit button by adding a regular button to your page and setting its **Link connects to** action to **Submit** in the [Connect Button](https://support.wix.com/en/article/working-with-the-connect-button-panel) panel. ## Sync (copy/overwrite) [Sync](https://support.wix.com/en/article/cms-accessing-and-syncing-your-sandbox-and-live-collections) allows you to move your data between your [Sandbox](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) and [Live](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) databases. You can copy selected or all items in a collection from a Sandbox collection to the live version of that collection. You can also overwrite a Sandbox collection with all the data from the live version, or overwrite all your Sandbox collections with the data from their live versions. You can only initiate sync operations from the Sandbox database.  Sandbox and Live are not always enabled for your collections. Learn more [here](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## System fields [System fields](https://support.wix.com/en/article/about-database-collections#system-fields) are fields that are added automatically to each collection, are hidden by default, and cannot be edited. System fields are ID, Date Created, Last Updated, and Owner.  ## Test site A [test site](https://support.wix.com/en/article/about-test-sites) is a version of your site that you may release to a percentage of your site visitors so that you can test that it's working before publishing. You can manage your test sites from the [Release Manager](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md). ## User input elements [User Input elements](https://support.wix.com/en/article/working-with-user-input-elements-2499809) allow you to capture and store user data in a [database collection](#database-collection). To use User Input elements, you need to connect them to a [dataset](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) whose [mode](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) is set to either Write-only or Read & Write. ## Validating Validating is the process of ensuring that data that visitors enter into a [user input element](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/velo-glossary.md) matches your criteria for that data and what you expect them to enter. You can validate user input with the [Settings panel](https://support.wix.com/en/article/validating-user-input-with-the-settings-panel) or with [code](https://support.wix.com/en/article/validating-user-input-with-code). ## Wix database app The Wix Database app is in the My Account section of your site. This is where you manage your [Live database](#live-database) collections. ## Web modules [Web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) enable you to write functions that run server-side in the backend, and easily call them in your client-side code. With web modules you can import functions from backend into files or scripts in public, knowing they will run server-side. Velo handles all the client-server communication required to enable this access. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Coding with Wix Studio ## Article: About Coding with Wix Studio ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md ## Article Content: # About Coding with Wix Studio > **Note:** Some of the features listed below are not yet available to all users. When you code with [Wix Studio](https://support.wix.com/en/article/about-wix-studio), you are working in an open development platform that accelerates the way you build web applications. You use [APIs](https://www.wix.com/velo/reference/spis/getting-started) to add custom functionality and interactions. You can code directly in Wix Studio's built-in [Code panel](https://support.wix.com/en/article/wix-studio-working-with-the-code-panel), in the [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide) (Wix's VS Code-based IDE), or with your own IDE and [integrating with GitHub](https://support.wix.com/en/article/velo-about-git-integration-wix-cli). Regardless of how you choose to code, you can enjoy serverless coding, all in an open, extendable platform. ## Coding features and functionality This section highlights key features and functionality for coding with Wix Studio's open platform. * **Coding on our open platform.** First, familiarize yourself with [our platform](https://support.wix.com/en/article/about-velo-by-wix). * **Different environments for coding.** Wix studio offers different [options for coding](https://support.wix.com/en/article/about-coding-with-wix-studio#ways-to-code-with-wix-studio). You decide which option works based on what you need to do. * **CSS styling**. Add and customize styling for the site using [CSS](https://support.wix.com/en/article/studio-editor-about-css-editing) that was developed outside of the Wix Studio ecosystem. * **Concurrent editing**. This feature helps you [collaborate efficiently](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) by allowing you and your teammates to work simultaneously on the same site. * **AI assistance**. The [Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md) helps you use artificial intelligence to get your code up and running quickly and efficiently. The assistant can help write, and even fix, your code. Receive responses in real time, including code snippets, to help you with your coding tasks. * **Service plugins**. You can integrate with service providers to further expand your site's functionality by replacing and extending existing app flows and/or by integrating external services with your site using [service plugins](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis). * **Headless sites and projects**. Design an independent site or project that takes advantage of Wix Studio's powerful infrastructure, tools, and capabilities in a [headless](https://dev.wix.com/docs/go-headless.md) environment. * **Databases**. Connect your site to [external database collections](https://support.wix.com/en/article/velo-integrating-external-databases-with-your-wix-site) in addition to the built-in [Wix Content Management System (CMS)](https://support.wix.com/en/article/cms-formerly-content-manager-an-overview). * **Functional Testing**. The [Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) tool allows you to test backend code without having to trigger it from the frontend. You can define custom parameters to use in testing backend functions. * **Packages**. This feature lets you choose a [package](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) with the functionality you want and add it to your site(s). You can install [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) and [packages built by Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md). * **Blocks**. You can [add code](https://support.wix.com/en/article/wix-blocks-about-coding-in-blocks) to your Wix Blocks widgets and apps. Because of this integration, [Blocks apps](https://support.wix.com/en/article/blocks-unboxed-a-quick-tour) can be anything from simple widget layouts to full-fledged interactive web components, complete with APIs and code files. * **Custom Apps**. You can create [custom apps](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-custom-apps.md) using Blocks, and add them to your site. This provides you with the ability to write code with specific functionality and use it across multiple sites. * **Developer tools**. A set of [developer tools](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) is available to help you monitor, test, and debug the code on your site(s). * **Test sites**. As you develop your site's functionality, you may want to test a version of your site on a percentage of your visitors. * **And here are some open-platform features for Wix Studio coders**: * [Fetch](https://support.wix.com/en/article/velo-accessing-3rd-party-services-with-the-fetch-api) for calling external APIs. * [HTTP functions](https://support.wix.com/en/article/velo-exposing-a-site-api-with-http-functions) for exposing your site's functionality as an API. * [Routers](https://support.wix.com/en/article/wix-studio-creating-a-router) for handling incoming requests to your site's pages/sitemap and directing the user to a custom page, according to the logic in the code. ## Ways to code with Wix Studio Wix is committed to making sure you, as a developer, have the right environment, tools, and APIs to develop sites and apps for your customers. We know coding is not a 1-size-fits-all experience. With Wix Studio, you choose how and where to code: + With the built-in Code panel + Using the Wix IDE + In your own local IDE and integrate with GitHub While previewing and publishing your site is always done in the Wix Studio Editor, you can use each coding option at different times in the development flow. For example, you can add properties to elements and add pages to your site using the Code panel, but can then switch to the Wix IDE for coding. ![Studio features](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2d4a280cc4f8dc3ac66dcda4928da0c0.png) ### Built-in Code panel To develop small projects that focus mainly on custom interactions and CSS styling on the frontend, craft your code using Wix Studio's built-in Code panel.
To start, click the ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/b5b2d7b8-0a53-4239-9e87-0e72bd8189b8) and click **Start Coding**. The resulting [Code panel](https://support.wix.com/en/article/wix-studio-working-with-the-code-panel) contains: + **The Code sidebar**. *What you want to code*—page code, global page code, CSS styling, and more. + **The Code panel**. *How you code*. The Code panel provides you with: + The **Code sidebar** for navigation between Wix Studio coding features. + The **code editor** for writing and editing your code. + The **Properties & Events panel** for naming elements, setting properties, and defining event handlers. + The **CSS Classes panel** for CSS styling on a specific element.

Watch a demo of how to work with the Code panel in Wix Studio: `youtube:https://www.youtube.com/watch?v=p3A2PD-jZ_E` ### Wix IDE For larger, multi-developer projects that involve more complex frontend code and/or backend code, use the online [Wix IDE](https://support.wix.com/en/article/about-the-wix-ide). The Wix IDE is a VS Code-based IDE that allows you to edit your site's code in your browser—no downloading or configuration necessary. Use familiar VS Code features to get up and running quickly.
Click **Code in Wix IDE** from the code editor to start coding.

### Your own local IDE and GitHub integration Use the [Git Integration and Wix CLI for Sites](https://support.wix.com/en/article/velo-about-git-integration-wix-cli) set of power tools to write, test, and publish code for your Wix site locally on your computer.
To [set this up](https://support.wix.com/en/article/velo-setting-up-git-integration-wix-cli), connect your site to GitHub, develop in your favorite IDE, test your code in real time, and publish your site from the command line.

> **Note:** The Wix IDE can't be used together with GitHub Integration. You can, however, connect your site to GitHub to work locally, and then disconnect from GitHub to resume working in the Wix IDE without losing any work. ## What each coding option offers Use this table to decide which Wix Studio coding option is for you based on the features provided. | | Code panel | Wix IDE | Git integration | | :-------: | :---------------------: | :-------: | :---------------------: | | CMS interaction | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Serverless | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Secure | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Open platform | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | NPM Packages | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Velo Packages | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | | Frontend code | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | CSS styling for all site pages | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | CSS styling for specific elements | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Backend code | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Concurrent editing | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | | Collaboration (team work) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355)


For >1 designers and 1 developer | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Public files | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Online access | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | | Offline access | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Complex projects or projects that require a lot of code | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | | Autosave by default | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | User-set | | Wix AI Assistant | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ![Supported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/1cc99729-151c-4c01-9a4c-09875a7dc355) | ![NotSupported](https://github.com/wix-incubator/wix-code-docs/assets/50321691/0b195e9c-ed04-44d1-bb69-5747cf39afd0) | ## Finding your way The following table lists features you are familiar with in the Wix Editor and directs you to the equivalent features in Wix Studio. | Feature | Wix Editor | Wix Studio | | ------ | ------ | ------ | | Enabling dev mode | From the Dev Mode menu, click

![Turn on Dev Mode](https://github.com/wix-incubator/wix-code-docs/assets/50321691/5ebeb184-e01f-423c-889d-7b8a06a97c66) | Coding is always available, no need to enable. Click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and ![Start Coding](https://github.com/wix-incubator/wix-code-docs/assets/50321691/37ce59a8-1a3d-44a7-a0cc-05ceff8b98b1). | | Previewing your site | On the site, click **Preview** at the top right. | On the site, click ![Preview icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/e65d33fd-497b-430a-8719-ff4d10488773) at the top right. | | Page code | In the Code sidebar under Page Code. | **Code panel**: In the Code sidebar under Page Code.


**Wix IDE and GitHub integration**: In the Explorer, navigate to `src/pages`, `src/public`, and `src/styles`. | | Public code | In the Code sidebar under Public Code. | **Code panel**: Under the Public section of Public & Backend in the Code sidebar.


**Wix IDE and GitHub Integration**: In the Explorer, navigate to `src/public`. | | Master page | The `masterpage.js` file is in the Code sidebar under Page Code. | **Code panel**: The `masterpage.js` file is in the Main Pages section under Page Code in the Code sidebar.


**Wix IDE and GitHub integration**: In the Explorer, navigate to `src/pages`. | | CSS for all site pages | Limited release. | **Code panel**: The `global.css` file is in the CSS section under Page Code in the Code sidebar.


**Wix IDE and GitHub integration**: In the Explorer, navigate to `src/styles`. | | CSS Classes panel for specific elements | NA | **Code panel**: The CSS Classes panel is at the bottom right under the code editor. | | Properties & Events panel | Click ![Properties & Events](https://github.com/wix-incubator/wix-code-docs/assets/50321691/507e23d9-1af2-4d91-9cd4-3c8e94531bf3) in the Code panel toolbar. The panel displays on the bottom right. | The Properties & Events panel is at the bottom left of the code editor, next to the Properties & Events panel. | | Backend code | In the Code sidebar under Public & Backend. | **Code panel**: Under the Backend section of Public & Backend in the Code sidebar.


**Wix IDE and GitHub integration**: In the Explorer, navigate to the `src/backend` folder. Here you will see web method files, `data.js`, `routers.js`, `events.js` (for backend event handlers), `http-functions.js`, `jobs.config` (for scheduling recurring jobs), and other general Javascript (`.js`) backend files. | | Web module `.web.js` backend files | In the Code sidebar under Public & Backend | Under the Backend section of Public & Backend in the Code sidebar.


**Wix IDE and GitHub integration**: In the Explorer, add a web module file in the `src/backend` folder. For example, `src/backend.web.js`. | | Web module `.jsw` backend files (deprecated) | In the Code sidebar under Public & Backend. | Under the Backend section of Public & Backend in the Code sidebar.


**Wix IDE and GitHub integration**: In the Explorer, navigate to the `src/backend` folder to see web method files. | | Data hooks | Under the Backend section of Public & Backend in the Code sidebar, add a data hook `data.js` file. | Under the Backend section of Public & Backend in the Code sidebar, add a data hook `data.js` file.


**Wix IDE and GitHub integration**: In the Explorer, you can see a `data.js` file in the `src/backend` folder. | | Packages | In the Code sidebar, click ![Packages & Apps](https://github.com/wix-incubator/wix-code-docs/assets/50321691/468ecdd9-c5db-478f-8cc2-6ff24cdf6068) for [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) and Velo packages. | **Code panel**: In the Code sidebar, click **Package & Apps** for npm packages and Velo packages. | | Custom apps | Under Custom Apps in the Package & Apps section of the Code sidebar. | **Code panel**: Under Custom Apps in the Package & Apps section of the Code sidebar. | | Service plugins | In the Code sidebar under Public & Backend. | **Code panel**: Under Custom Apps in the Package & Apps section of the Code sidebar. | | Wix AI Assistant | NA | **Wix IDE sidebar**: Click ![AI](https://github.com/wix-incubator/wix-code-docs/assets/50321691/b269b906-908c-48a7-b000-8901dbc75e7c). | | GitHub integration | In the Code sidebar, click ![GitHub](https://github.com/wix-incubator/wix-code-docs/assets/50321691/50635f4a-7600-44eb-9f3a-16fa3ad878fe) and then ![Connect to GitHub](https://github.com/wix-incubator/wix-code-docs/assets/50321691/10c71e4c-2ae0-4187-96fe-1bd6d2fbbca0). | **Code panel**: In the Code sidebar, click **GitHub** and then ![Connect to GitHub](https://github.com/wix-incubator/wix-code-docs/assets/50321691/10c71e4c-2ae0-4187-96fe-1bd6d2fbbca0) | | Routers | In the Code sidebar, under the Page Code's Main Pages section, add a router.

Enter a URL prefix for your router and click **Add & Edit Code**. This action not only adds router pages to your Page Code, but this action also automatically adds a `routers.js` file to your site's Backend. | **Code panel**: In the Code sidebar, under the Page Code's Main Pages section, add a router.

Enter a URL prefix for your router and click **Add & Edit Code**. This action not only adds router pages to your Page Code, but this action also automatically adds a `routers.js` file to your site's Backend.


**Wix IDE and GitHub integration**: In the Explorer, you can see your router pages in the `src/pages` folder, and a `router.js` file in the `src/backend` folder. | | Dashboard Page | In the Add panel under **Page > Dashboard page**. | In the Pages panel on the left, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/04083fa4-3262-4606-98d1-cbc359bf8585/1a26226e-c281-42d2-8a60-88e44fe40c61.png) , and then click **Add** under **Dashboard Page**. | | Dev tools | Accessible from the Code sidebar ![Velo-sidebar](https://github.com/wix-incubator/wix-code-docs/assets/50321691/68231f51-1e67-49ed-bbfd-2005679312e1) or the dashboard. | Accessible from the Developer Tools section of the Code sidebar, or the dashboard. | | Test sites | On the site, click **Site** and then **Deploy Test Site**. | Click ![Main](https://github.com/wix-incubator/wix-code-docs/assets/50321691/6eeb5c1d-d0ad-464b-a225-d5739a3e0271) at the top left, **Site**, and **Create Test Site**. | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Copying and Deleting Coded Sites, Pages, and Elements ## Article: Copying and Deleting Coded Sites, Pages, and Elements ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/copying-and-deleting-coded-sites-pages-and-elements.md ## Article Content: # Managing Coded Sites, Pages, and Elements When working on a Wix site with code, it's important to understand how duplication and deletion affect your site's functionality. Whether you're copying an entire site, a page, or an individual element, or deleting elements with connected code, the outcome depends on how your code is structured and wired. ## Duplicating pages and sites You can duplicate [sites](https://support.wix.com/en/article/duplicating-your-site-1472847) and [pages](https://support.wix.com/en/article/wix-editor-managing-your-sites-pages), as well as [clone](https://support.wix.com/en/article/wix-studio-managing-sites-in-a-workspace) and [transfer](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) your Wix sites. Whether you're duplicating a single page, an entire site, or cloning or transferring a site, the code and events are maintained in the new version. ## Duplicating elements When you copy and paste an element that has associated code, the element itself is duplicated, but any events, wiring, or code linked to it aren't included.
**Important:** If you're building a Wix site that others can edit, make sure they understand the limitations of working with elements. For more information, see [Working with Collaborators](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/working-with-collaborators.md).
## Deleting elements When you delete elements from your site, the behavior of any associated code depends on the type of event handler used — static or dynamic. In both cases, the code itself isn't automatically removed, but how it's connected to the element changes. >**Note:** In the editor, if a collaborator tries to delete elements referenced by code, a confirmation prompt displays. The prompt helps prevent accidental deletions that can impact your code. The prompt displays for: > >- Elements directly referenced by code. A ![code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/061bba0b7714af979fc4343df936a412.png) icon displays for these elements as an indicator. >- Parent elements whose nested elements are referenced by code. ### Deleting an element with dynamic event handler When you delete an element with a [dynamic event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md), the associated code isn't removed. If you add a new element and give it the same ID as the deleted one, the existing code will continue to work as expected. ### Deleting an element with static event handler When you delete an element with [static event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-static-event-handlers.md), the associated code remains, but the event wiring is removed. For example, you have an element named `button2` with an `onClick` event that toggles the visibility of an element called `image3`: ```javascript export function button2_onClick(event) { if ($w("#image3").visible) { $w("#image3").hide(); } else { $w("#image3").show(); } } ``` If you delete `button2` from your page, the event code is not deleted. However, the event is no longer wired. This means that if you add a new button and name it `button2`, the code above won't run when it's clicked, you would need to rewire the event manually. ## See also - [About Code Placement](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md) - [Working with Collaborators](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/working-with-collaborators.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Collaborators ## Article: Working with Collaborators ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/working-with-collaborators.md ## Article Content: # Working with Collaborators When you develop a site, you may want other people to collaborate on the site or you may want to transfer the site to someone else but maintain your ability to collaborate on it. ## Allow other people to collaborate on your site You can let other people collaborate on your site's code and functionality by adding them as [collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) to your site. It isn't necessary to transfer ownership of the site to them. Collaborators need to be assigned a role that has **Edit Site** permissions to be able to edit the site and add or edit code. It is not necessary to give a collaborator **Publish** permissions for them to be able to edit the site code. Note that if the collaborator adds functionality that can only be tested on the live site, they won't be able to test it. ## Transfer ownership and collaborate Some features are available to test only on a live site. You have to have access to the site so you can test and maintain the code you developed, even if it's for other users on their domain.  You also may want to do more development that needs testing after the site has been published and is live. You probably don't want visitors to the site to access the pages that are under development and being tested. To test and maintain a site developed for another user: 1. When a site is ready for live testing, [transfer ownership](https://support.wix.com/en/article/transferring-a-premium-site-to-another-wix-account) of the domain to the other user. 2. Instruct the other user to [invite you as a site collaborator](https://support.wix.com/en/article/inviting-site-contributors). 3. Ensure that you are added as a role that has **Edit Site** permissions from the options for [collaborator roles](https://support.wix.com/en/article/roles-permissions-overview#roles637). This allows you full access to the site, including its backend files and database collections. To continue development on a live site once ownership is transferred: 1. If you are working on a new page, [add a page](https://support.wix.com/en/article/adding-a-page-to-your-site). If you are working on an existing page, [duplicate the page](https://support.wix.com/en/article/duplicating-a-page).  2. Make sure the page is [hidden](https://support.wix.com/en/article/hiding-a-page-from-the-menu) in the menu so site visitors won't be able to access it while it's being developed and tested. (Note that a hidden page on a published site can still be accessed by modifying the URL.) 3. Once you are finished with development and testing, [make the page visible](https://support.wix.com/en/article/hiding-and-unhiding-a-page-from-your-site-menu#unhiding-a-page-in-your-site-menu). If it replaces a page, [delete](https://support.wix.com/en/article/deleting-a-page) the old page.  The following are some considerations for when different users work on a site developed in the editor.  ## Considerations when working on a live site for another user If you develop a site with code for someone else that you can both edit, you need to understand the limitations of what you or the other user can do with the site and its elements. Keep in mind, for example, the other user might be a designer with no coding experience. In this section, we describe: - [Site-related considerations](#site-related-considerations) - [Element-related considerations](#element-related-considerations) ### Site-related considerations If you duplicate an individual page or an entire site, or if you or the other user clones or transfers a site, the code and events in the site are maintained only in the new copy of the page or site. ### Element-related considerations * If you or they copy and paste an element that has event handlers associated with it, the new element will not work as expected since the event wiring and code is not copied.  * If you or they copy and paste an element that is connected to a dataset, the new element maintains the connection settings from the element it was copied from. * If you or they move an element from one page to another, the element will not work as expected since the associated code and wiring is on the original page and is not moved to the new page. * If an element is connected to a dataset, and both are set to show on only one page, if the element is then set to [Show on All Pages](https://support.wix.com/en/article/showing-an-element-on-all-pages-6533570), the connection to the dataset is broken. * In the editor, if a collaborator tries to delete elements referenced by code, a confirmation prompt displays. The prompt helps prevent accidental deletions that can impact your code. The prompt displays for: + Elements directly referenced by code. A ![code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b2e602834297db1e0836bd83e1de9f8a.png) icon displays for these elements as an indicator. + Parent elements whose nested elements are referenced by code. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Error Log When Publishing ## Article: Publishing Your Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/error-log-when-publishing.md ## Article Content: # Error Log When Publishing You can [publish your site](https://support.wix.com/en/article/wix-editor-publishing-your-site) at any time from the top toolbar of the editor. When you click **Publish**, Wix performs an automatic check on the site's code for any errors. If errors are detected, Wix alerts you and provides the option to either return to the editor without publishing your site or to publish your site with the errors. > **Note:** > Wix only reports code errors if there have been changes to the code since the last time the site was published. If no modifications were made to the code, Wix publishes your site without requiring any additional confirmation. ## Viewing the error log
![image of popup with dev mode on](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1ee0549eb4f062ecca3bf3b7e3395af3.png)
If developer mode is enabled when you publish your site, Wix provides details of the errors in your code. You can then access the most recent `build.log` file to analyze where errors have been detected. To do this, click on **View Error Log** in the publishing prompt. Later, open the `build.log` file directly in your Wix editor under **Developer Tools** to review: image of latest build opening build.log file Note that the `build.log` file is only kept for 30 days. After 30 days, the status for Latest Build under **Developer Tools** will be `Awaiting publish`. Publishing your site generates a new `build.log` file.
image of latest build with an Awaiting publish message
## What Wix checks During the automatic check, Wix analyzes your code files and validates the syntax that may prevent the bundling of code files. For example, Wix checks [`no-undef`](https://eslint.org/docs/latest/rules/no-undef) and [`no-const-assign`](https://eslint.org/docs/latest/rules/no-const-assign) rules to flag the use of undeclared variables or if `const` variables were reassigned. The files it checks are: - Page files. - [Web modules.](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) - `.js` files, including [events.js](https://support.wix.com/en/article/velo-backend-events), [data.js](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks/.md), [http-functions.js](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md), and [routers.js](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md#routing-code). The files it does not check are: - Certain service plugin files. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Develop Websites with the SDK ## Article: Develop Websites with the SDK ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md ## Article Content: # Develop Websites with the SDK You can now use the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md) instead of Velo APIs for most functionality when developing sites or building apps with Blocks. This marks the beginning of a gradual transition from using Velo APIs to using the next generation SDK. You are encouraged to start using the SDK in all new and ongoing development projects. You may also want to [migrate existing code](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/migrate-from-velo-to-the-sdk.md) written using Velo APIs to the SDK. ## Benefits to using the SDK Wix has chosen to invest its future efforts in the Wix JavaScript SDK for the following reasons: - **Industry standards**: The SDK follows current industry standards for web development, such as the use of npm modules. This lowers the barrier to entry for professional web developers. - **Stability**: The use of npm modules allows us to provide a better versioning experience. Once you have your code working with a specific version of a module, you can be sure that your code will always continue to work with that version. This also puts you in control of when and where you want to use updated modules. - **Cross-Wix alignment**: The SDK is already used by Wix App and Wix Headless developers when building their projects. It is also used internally by Wix's own developers. Now that site developers will use the SDK as well, Wix can increase the efficiency of the rollout process for new functionality and documentation. This alignment also means that site developers will have an easier time creating apps and headless projects, should they choose to do so. ## Transition Process At the current stage in the process of transitioning from Velo APIs to the SDK: - All Velo APIs will continue to work as usual. - The SDK will receive all new functionality updates. - The Velo API will continue to receive updates to the `$w` and `$widget` APIs. - All other Velo APIs will not receive any more updates. - Some functionality in the Velo API is [not yet supported in the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). - Some documentation in this portal, including tutorials and code examples, still uses Velo APIs for the time being. - The Website Dev Assistant still uses Velo APIs in its responses. - The [Velo API reference](https://dev.wix.com/docs/velo.md) has been restructured to indicate which API modules are currently transitioning to the SDK. In future stages of the transition process: - All Velo functionality will be supported in the SDK. - The documentation in this portal will use the SDK in tutorials and code examples. - The Website Dev Assistant will use the SDK in its responses. - Velo APIs will be deprecated. ## See also - [Velo to SDK API Mapping](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md) - [Migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/migrate-from-velo-to-the-sdk.md) - [SDK: About Site Development](https://dev.wix.com/docs/sdk/articles/get-started/about-site-development.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Velo to SDK API Mapping ## Article: Velo to SDK API Mapping ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md ## Article Content: # Velo to SDK API Mapping You can now use the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md) instead of the Velo API for most functionality when developing sites or building apps with Blocks. Learn more about [developing websites with the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md). Wix's transition to the SDK is a gradual process. At this stage, not all Velo APIs have equivalent SDK modules that can be used for site development or for app creation with Blocks. For functionality that is not yet supported in the SDK, use Velo APIs alongside the SDK. This article outlines which [Velo APIs you should continue to use](#velo-apis-you-should-continue-to-use), and how to [find your Velo APIs' functionality in the SDK](#finding-sdk-replacements-for-velo-apis). ## Velo APIs you should continue to use The Velo APIs that should still be used at this stage fall into 2 categories: - **Velo-Only APIs**: These APIs are imported from Velo modules whose functionality the SDK doesn't support. You can find these APIs in the [Velo-Only APIs](https://dev.wix.com/docs/velo/velo-only-apis/about-velo-only-apis-and-the-sdk.md) section of the Velo API reference. - [`$w`](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) - [`$widget`](https://dev.wix.com/docs/velo/velo-only-apis/$widget/introduction.md) - [`wix-configs-backend`](https://dev.wix.com/docs/velo/velo-only-apis/wix-configs-backend/introduction.md) - [`wix-http-functions`](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md) - [`wix-realtime-backend`](https://dev.wix.com/docs/velo/velo-only-apis/wix-realtime-backend/introduction.md) - [`wix-application`](https://dev.wix.com/docs/velo/velo-only-apis/wix-application/introduction.md): Most `wix-application` functionality isn't yet available in the SDK. The SDK currently supports only identifying the app instance, similar to what [`getDecodedAppInstance()`](https://dev.wix.com/docs/velo/velo-only-apis/wix-application/get-decoded-app-instance.md) provides in Velo. To identify the app instance using the SDK: - In backend code, [call `getTokenInfo()` or `getAppInstance()` in a backend function](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/identify-the-app-instance-in-backend-environments.md#blocks-backend-function). - In frontend code, [simply call your backend function](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/identify-the-app-instance-in-frontend-environments.md#blocks-backend-function). - [`wix-editor`](https://dev.wix.com/docs/velo/velo-only-apis/wix-editor/introduction.md) - **Events and Service Plugins**: These APIs work by file and name conventions. They may have SDK counterparts, but the SDK versions don't work for site development and for app creation with Blocks. You can find them in the [Events & Service Plugins](https://dev.wix.com/docs/velo/events-service-plugins/about-events-service-plugins-and-the-sdk.md) section of the Velo API reference. ## Finding SDK replacements for Velo APIs The functionality of the Velo APIs listed in the [APIs](https://dev.wix.com/docs/velo/apis/about-velo-apis-and-the-sdk.md) section of the Velo API reference are mostly supported by the SDK. These APIs each fall into one of the following categories: - [Universal APIs](#universal-apis) - [Backend APIs](#backend-apis) - [Frontend APIs](#frontend-apis) - [Unique cases](#unique-cases) To learn how to migrate your code from Velo to SDK, see [Migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/migrate-from-velo-to-the-sdk.md). ### Universal APIs Velo APIs that end in `.v2`, such as `wix-bookings.v2`, are universal APIs. [`wix-blog-backend`](https://dev.wix.com/docs/velo/apis/wix-blog-backend/introduction.md), [`wix-ecom-backend`](https://dev.wix.com/docs/velo/apis/wix-ecom-backend/introduction.md), [`wix-pro-gallery-backend`](https://dev.wix.com/docs/velo/apis/wix-pro-gallery-backend/introduction.md), and [`wix-tags.v1`](https://dev.wix.com/docs/velo/apis/wix-tags-v1/introduction.md) are also universal APIs. Universal APIs have direct equivalents in the SDK. These are the backend SDK modules with similar naming to the Velo counterparts. For example, the [`bookings`](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md) SDK module is a direct replacement for the Velo [`wix-bookings.v2`](https://dev.wix.com/docs/velo/apis/wix-bookings-v2/introduction.md) API.
Exceptions: - [`wix-secrets-backend.v2`](https://dev.wix.com/docs/velo/apis/wix-secrets-backend-v2/introduction.md): This is a [backend API](#backend-apis), but it does have an equivalent SDK module.
> **Note:** Because the Wix SDK continues to be updated, an equivalent SDK module may have more functionality than its equivalent Velo API, so they may not look identical. ### Backend APIs Velo APIs that end in `-backend`, such as `wix-bookings-backend`, are backend APIs. Most backend APIs have corresponding [universal APIs](#universal-apis) that contain the same functionality. For example, `wix-bookings.v2` contains `wix-bookings-backend` functionality. To migrate your code that uses backend APIs to the SDK, use the corresponding universal API's equivalent SDK module. For example, use the [`bookings`](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md) SDK module instead of the Velo [`wix-bookings-backend`](https://dev.wix.com/docs/velo/apis/wix-bookings-backend/introduction.md) API. Backend Velo APIs use the `suppressAuth` property to prevent permission checks from running. The equivalent functionality in the SDK is elevation. Learn more about [elevated permissions](https://dev.wix.com/docs/api-reference/articles/authentication/about-elevated-permissions.md).
Exceptions: - [`wix-blog-backend`](https://dev.wix.com/docs/velo/apis/wix-blog-backend/introduction.md): This is a [universal API](#universal-apis). - [`wix-cache-backend`](https://dev.wix.com/docs/velo/apis/wix-cache-backend/cache/introduction.md): The [`cache`](https://dev.wix.com/docs/api-reference/site/viewer/cache/invalidate-cache.md) SDK module contains this API's functionality. - [`wix-chat-backend`](https://dev.wix.com/docs/velo/apis/wix-chat-backend/introduction.md): The [`inbox.messages`](https://dev.wix.com/docs/api-reference/crm/communication/inbox/example-flows.md) SDK submodule contains this API's functionality. - [`wix-ecom-backend`](https://dev.wix.com/docs/velo/apis/wix-ecom-backend/introduction.md): This is a [universal API](#universal-apis). - [`wix-pay-backend`](https://dev.wix.com/docs/velo/apis/wix-pay-backend/introduction.md): This API doesn't currently have an SDK replacement. For now, continue using the Velo API. - [`wix-payment-provider-backend`](https://dev.wix.com/docs/velo/apis/wix-payment-provider-backend/introduction.md): The [`payments.pspCallbacks`](https://dev.wix.com/docs/api-reference/business-management/payments/payment-service-provider-service-plugin/callbacks/submit-event.md) SDK submodule contains this API's functionality. - [`wix-pro-gallery-backend`](https://dev.wix.com/docs/velo/apis/wix-pro-gallery-backend/introduction.md): This is a [universal API](#universal-apis). - [`wix-site-backend`](https://dev.wix.com/docs/velo/apis/wix-site-backend/introduction.md): Migrating this API is a [unique case](#wix-site-backend).
### Frontend APIs Velo APIs that end in `-frontend`, such as `wix-bookings-frontend`, are frontend APIs. [`wix-mobile`](https://dev.wix.com/docs/velo/apis/wix-mobile/introduction.md) and [`wix-navigate-mobile`](https://dev.wix.com/docs/velo/apis/wix-navigate-mobile/introduction.md) are also frontend APIs. Frontend APIs have direct equivalents in the SDK. These are the frontend SDK modules with similar naming to the Velo counterparts. For example, the [`bookings`](https://dev.wix.com/docs/sdk/frontend-modules/bookings/introduction.md) SDK frontend module is a direct replacement for the Velo [`wix-bookings-frontend`](https://dev.wix.com/docs/velo/apis/wix-bookings-frontend/introduction.md) API. Frontend SDK methods are asynchronous, even when the Velo equivalent is synchronous.
Exceptions: - [`wix-pay-frontend`](https://dev.wix.com/docs/velo/apis/wix-pay-frontend/currencies/introduction.md): Migrating this API is a [unique case](#wix-pay-frontend). - [`wix-storage-frontend`](https://dev.wix.com/docs/velo/apis/wix-storage-frontend/introduction.md): This API's equivalent SDK module, [`storage`](https://dev.wix.com/docs/sdk/frontend-modules/storage/introduction.md), has asynchronous methods. The equivalent Velo methods are synchronous.
### Unique cases The following Velo APIs have a unique way to implement their functionality in the SDK. #### `wix-auth` The [`wix-auth`](https://dev.wix.com/docs/velo/apis/wix-auth/introduction.md) Velo API has equivalent functionality in the [`essentials.auth`](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md) SDK submodule. #### `wix-bookings.v1` The `wix-bookings.v1` Velo API has a corresponding [universal API](#universal-apis), [`wix-bookings.v2`](https://dev.wix.com/docs/velo/apis/wix-bookings-v2/introduction.md). Therefore, use the the [`bookings`](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md) SDK module instead of `wix-bookings.v1`. #### `wix-dashboard` The [`wix-dashboard`](https://dev.wix.com/docs/velo/apis/wix-dashboard/introduction.md) Velo API has an equivalent [`dashboard`](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) SDK module. This module is a [host module](https://dev.wix.com/docs/sdk/host-modules/about-host-modules.md). #### `wix-data` The [`wix-data`](https://dev.wix.com/docs/velo/apis/wix-data/introduction.md) Velo API has a replacement [`items`](https://dev.wix.com/docs/sdk/business-solutions/data/items/introduction.md) SDK submodule in the `data` SDK module. Note that while this submodule closely mirrors `wix-data`, it's not a direct equivalent. Learn more about [migrating from `wix-data` to the Data Items SDK](https://dev.wix.com/docs/velo/apis/wix-data/migrate-to-the-sdk.md). #### `wix-fetch` Use the native `fetch()` to access the functionality of [`wix-fetch`](https://dev.wix.com/docs/velo/apis/wix-fetch/introduction.md) in the SDK. The `wix-fetch` Velo API has no SDK replacement. This API contains 2 methods: - `fetch()`: You can use the native `fetch()` by calling this method on the frontend or backend without importing it when using the SDK. - `getJSON()`: This method does not exist in the SDK. You can convert your `getJSON()` calls to `fetch()` instead. To get the JSON data from the `fetch()` response, call the `json()` method. #### `wix-pay-frontend` The [`wix-pay-frontend`](https://dev.wix.com/docs/velo/apis/wix-pay-frontend/introduction.md) Velo API has 2 sections and each section is handled differently: - [`startPayment()`](https://dev.wix.com/docs/velo/apis/wix-pay-frontend/start-payment.md): This Velo method doesn't currently have an SDK replacement. For now, continue using the Velo method. - [`currencies`](https://dev.wix.com/docs/velo/apis/wix-pay-frontend/currencies/introduction.md): This Velo API has an equivalent [`pay.currencies`](https://dev.wix.com/docs/sdk/frontend-modules/pay/currencies/introduction.md) SDK frontend submodule. #### `wix-search` The [`wix-search`](https://dev.wix.com/docs/velo/apis/wix-search/introduction.md) Velo API doesn't currently have an SDK replacement. For now, continue using the Velo API. #### `wix-site-backend` The [`wix-site-backend`](https://dev.wix.com/docs/velo/apis/wix-site-backend/introduction.md) Velo API has 2 sections and each section is handled differently: - [`invalidateCache()`](https://dev.wix.com/docs/velo/apis/wix-site-backend/invalidate-cache.md): This Velo method doesn't currently have an SDK replacement. For now, continue using the Velo method. - [`GeneralInfo`](https://dev.wix.com/docs/velo/apis/wix-site-backend/general-info/introduction.md): This functionality can be implemented using [`getSiteProperties()`](https://dev.wix.com/docs/api-reference/business-management/site-properties/properties/get-site-properties.md) from the `business-tools.siteProperties` SDK submodule. #### `wix-site-plugins.v1` The [`wix-site-plugins.v1`](https://dev.wix.com/docs/velo/apis/wix-site-plugins-v1/introduction.md) Velo API has an equivalent [`site-plugins`](https://dev.wix.com/docs/api-reference/app-management/site-plugins/placement-status-v1/introduction.md) SDK module. #### `wix-web-module` The [`wix-web-module`](https://dev.wix.com/docs/velo/apis/wix-dashboard/introduction.md) Velo API has an equivalent [`web-methods`](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction.md) SDK module. This module is a core module. #### `wix-widget` The [`wix-widget`](https://dev.wix.com/docs/velo/velo-only-apis/wix-widget/introduction.md) Velo API has an equivalent [`widget`](https://dev.wix.com/docs/sdk/host-modules/editor/widget/introduction.md) SDK submodule. #### Velo packages [Velo packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) don't currently have equivalent SDK packages. In the future, equivalent SDK packages will gradually be released. ## See also - [Develop Websites with the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md) - [Migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/migrate-from-velo-to-the-sdk.md) - [About Site Development](https://dev.wix.com/docs/api-reference/articles/platform-overview/about-wix-site-development.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Migrate from Velo to the SDK ## Article: Migrate from Velo to the SDK ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/migrate-from-velo-to-the-sdk.md ## Article Content: # Migrate from Velo to the SDK You can now use the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md) instead of the Velo API for most functionality when developing sites or building apps with Blocks. You are encouraged to start using the SDK in all new and ongoing development projects. Although your existing code written using the Velo API will continue to work, you may want to migrate that code to use the SDK. This article explains how to migrate your code for Velo APIs that have equivalent SDK modules. ## Step 1 | Identify what to migrate In many cases, the SDK version of a method works exactly the same as the Velo version and no further changes are necessary. However, in some cases the SDK method may work slightly differently. This is especially true if you were using an older version of a Velo API. If there are any differences, you will need to address them in your code. This usually means you need to pass different information when calling a method or handle the method’s response differently. Velo APIs fall into the following categories: **APIs that have equivalent SDK modules**: Migrate your code using the instructions in this article. **APIs that don't have equivalent SDK modules, but the SDK supports their functionality**: Rewrite your code using SDK modules that support the APIs' functionality. **APIs for which the SDK doesn't yet support their functionality**: Continue to use these Velo APIs alongside the SDK. To learn more about finding Velo APIs' functionality in the SDK and which Velo APIs you should continue to use, see [Velo to SDK API Mapping](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). ## Step 2 | Install packages The SDK is available through public npm packages. Each module in the SDK has its own package. 1. Determine which packages you need to install. Check the methods you use in the [SDK reference](https://dev.wix.com/docs/sdk.md) to see which packages you need. Each module’s introductory documentation specifies the package you need to install. 2. Install the packages you need using the [editor](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md#install-an-npm-package) or the [Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md#install-an-npm-package). ## Step 3 | Adjust import statements As you need to import from the SDK package instead of from a Velo module, you need to adjust your existing import statements. For example to create a booking using Velo, you import `bookings` from `wix-bookings.v2`: ```javascript import { bookings } from 'wix-bookings.v2'; //... const booking = await bookings.createBooking(bookingInfo); ``` To create a booking using the SDK, you import `bookings` from `@wix/bookings`: ```javascript import { bookings } from '@wix/bookings'; //... const booking = await bookings.createBooking(bookingInfo); ``` In this case, you simply need to change the source of the import from `wix-bookings.v2` to `@wix/bookings`. ## See also - [SDK: About Site Development](https://dev.wix.com/docs/sdk/articles/get-started/about-site-development.md) - [Velo: About Using the SDK](https://dev.wix.com/docs/velo/articles/api-overview/about-using-the-sdk.md) - [Velo to SDK API Mapping](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Identities ## Article: Identities ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/identities.md ## Article Content: # About Identities When a method is called on your site or on a [custom dashboard page](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/dashboard-admin-pages/about-dashboard-pages.md), it is called by a specific identity. This identity represents the current site user who triggers the method call, regardless of whether the code making the call is in the frontend, the backend, or on a custom dashboard page. Some Velo methods are restricted based on which identities are authorized to call them. Authorization notes in the Velo API reference indicate when a method may be restricted by identity. If necessary, you can call such methods even when the current identity lacks permissions by using [elevation](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/elevation.md). > Note: Some methods may further restrict access by requiring that the calling identity has specific permissions granted through a role. You can use elevation to bypass these restrictions as well. The Velo API recognizes the following identities: - [Site visitor](#site-visitor) - [Site member](#site-member) - [Wix user](#wix-user) ## Site visitor Site visitors are visitors to a site who have not logged in. Methods that site visitors can call typically involve operations specific to their own experience on the site, such as: - Adding products to the current cart. - Creating a booking. - Copying text to the local clipboard. ## Site member Site members are visitors who have logged in. They can call all the same methods available to site visitors, plus additional methods that only apply to their site membership. Site members who are also [site owners or collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) are also considered admins. Their admin status allows them to call certain administrative methods that other members cannot call, such as methods to work with the files in the site’s Media Manager. ## Wix user When interacting with the site dashboard, site owners or collaborators take on the Wix user identity. Although Wix users are the same people as site owners and collaborators, they assume different identities depending on context. As the dashboard is a secured admin area, Wix users can call methods with stricter authorization requirements in the dashboard. Examples of administrative methods Wix users can call in the dashboard include: - Adding products to a store. - Confirming or canceling bookings. - Approving new members. > Note: Although calls made from a published dashboard page are called with the authorization of a Wix user, those same calls made from a dashboard page being previewed in the editor are only called with the authorization of a site member admin. This means that calls which will work in your published dashboard page may not work when previewing the same dashboard page. ## See more - [About Elevation](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/elevation.md) - [Roles & Permissions](https://support.wix.com/en/article/roles-permissions-overview) - [elevate()](https://dev.wix.com/docs/velo/api-reference/wix-auth/elevate.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Elevation ## Article: Elevation ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/elevation.md ## Article Content: # About Elevation > **Note:** This article discusses elevation when developing websites, but the concepts and implementation are the same when [coding in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/code-in-blocks/about-coding-in-blocks.md). Some Velo API methods are restricted based on the [identities](https://support.wix.com/en/article/roles-permissions-overview) and [roles](https://support.wix.com/en/article/roles-permissions-overview) authorized to call them. Elevation is a mechanism that allows you to call these restricted methods even when the calling identity lacks the necessary authorization. This process creates a temporary elevated version of a method by calling the elevate() method, enabling you to bypass the usual authentication checks that might otherwise restrict access. For example: ```javascript import { elevate } from "wix-auth"; import { someModule } from "wix-some-module"; //... const elevatedMethod = elevate(someModule.methodName); elevatedMethod(param1, param2); ``` ## Methods that may require elevation Methods can be restricted based on user [identity](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/identities.md) or [roles and permissions](https://support.wix.com/en/article/roles-permissions-overview). ### Identity restriction example An example of a method restricted by identity is the [`assignBadge()`](https://dev.wix.com/docs/velo/apis/wix-members-v2/badges/assign-badge.md) method. This method can only be called by [Wix users](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/identities.md#wix-user) because site members should not be able to assign badges to themselves. This means that `assignBadge()` can only be called without elevation in a published dashboard page, where the caller will have the Wix user identity. In all other cases, if you need to call `assignBadge()`, you must elevate it first. For example, you might want to automatically assign a badge to users who take some specific action or reach a predetermined milestone. Since the site members who earned the badge don’t have rights to assign it to themselves. In this case, you need to use elevation to assign the badge. ### Roles and permissions restriction example An example of a method restricted based on roles and permissions is the [`confirmBooking()`](https://dev.wix.com/docs/velo/apis/wix-bookings-v2/bookings/confirm-booking.md) method. This method can only be called by admin members with an administrative bookings role because users creating bookings should not be allowed to confirm their own bookings. If you need to call `confirmBooking()` on behalf of a user who does not have an administrative Bookings role, you must elevate it first. For example, you might have a specific service that you want to confirm automatically once a site visitor books it. Since site visitors who book the service don’t have permissions to confirm it themselves, you need to use elevation to confirm the booking. ## Security considerations Due to potential security risks, you can only elevate methods in backend code. While elevation offers flexibility, it’s crucial to consider how and when elevation is triggered. [Web methods](https://dev.wix.com/docs/velo/apis/wix-web-module/web-method.md) and [HTTP functions](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md) are particularly vulnerable if not properly managed due to their open nature. Elevation in [backend events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) or code only triggered from [scheduled jobs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/recurring-jobs/about-scheduling-recurring-jobs.md) presents less risk but should still be handled cautiously. ### Example To demonstrate how to properly handle elevation, consider a site that rewards frequent visitors with a special badge. To do so, the code needs to track recent member visits and call `assignBadge()` using elevation when a member has visited frequently enough. (Note that the code samples below have been simplified, removing error handling and other non-essential code.) Here is an example of an insecure approach to writing this code: ```javascript // Backend code in badges.web.js import { elevate } from "wix-auth"; import { Permissions, webMethod } from "wix-web-module"; import { badges } from "wix-members.v2"; export const assignBadge = webMethod( Permissions.Anyone, (badgeId, memberId) => { const elevatedAssignBadge = elevate(badges.assignBadge); return elevatedAssignBadge(badgeId, [memberId]); } ); export const isFrequentVisitor = webMethod(Permissions.Anyone, (memberId) => { // Query collection that tracks member visits, // determine if the specified member is a frequent visitor, // and return the result }); // Frontend code in masterPage.js import { assignBadge, isFrequentVisitor } from "backend/badges.web"; import { members } from "wix-members.v2"; const frequentVisitorBadgeId = "c705b8dd-aae2-4eea-a4d1-16f52421ec0a"; //... const currentMember = await members.getCurrentMember(); if (isFrequentVisitor(currentMember._id)) { assignBadge(frequentVisitorBadgeId, currentMember._id); } ``` There are several problems with the `assignBadge()` web method used in this approach: - It is open for anyone to call, even though only members can receive badges. - It doesn’t ensure that it will only assign the intended badge. - It doesn’t ensure that it will assign a badge to the currently logged in member. Because of these issues, this method can be called by a malicious user to assign any badge to any member. You can easily remedy these issues by being more careful about where you use elevation and how you expose it to be called. For example: ```javascript // In badges.web.js import { elevate } from 'wix-auth'; import { Permissions, webMethod } from "wix-web-module"; import { badges } from "wix-members.v2"; import { members } from 'wix-members.v2'; const assignFrequentVisitorBadge = webMethod( Permissions.Member, () => { const currentMember = await members.getCurrentMember(); if (isFrequentVisitor(currentMember.\_id)) { const frequentVisitorBadgeId = 'c705b8dd-aae2-4eea-a4d1-16f52421ec0a'; const elevatedAssignBadge = elevate(badges.assignBadge); return elevatedAssignBadge(frequentVisitorBadgeId, [memberId]); } } ) const isFrequentVisitor = (memberId) => { // Query collection that tracks member visits, // determine if the specified member is a frequent visitor, // and return the result } // masterPage.js import { assignFrequentVisitorBadge } from 'backend/badges.web'; //... assignFrequentVisitorBadge(); ``` In this approach, the following makes sure the elevation is not exploited by malicious users: - The ID of the badge to assign is specified in backend code. - The current user ID is retrieved in backend code. - The web method used to trigger the badge assignment has permissions set so it can only be called by site members. ## See more - [About Identities](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/identities.md) - [Roles & Permissions](https://support.wix.com/en/article/roles-permissions-overview) - [elevate()](https://dev.wix.com/docs/velo/apis/wix-auth/elevate.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: JavaScript Support ## Article: JavaScript Support ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/java-script-support.md ## Article Content: # About JavaScript Support Wix supports working in JavaScript to develop websites, as well as some special features, including: * Support for JavaScript features * Support for modules * Support for the JavaScript Fetch API * Sharing JavaScript code naturally between the backend and frontend using [web modules](https://support.wix.com/en/article/how-to-call-server-side-code-from-the-front-end) ## Supported IDEs You can work in JavaScript in: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## JavaScript feature support (ECMAScript 2020) Wix supports modern JavaScript features up through and including the [ES2020](https://tc39.es/ecma262/2020/) standard. You can use promises, async/await, and arrow functions, which were introduced with ES2017. Browsers are gradually adopting the ES2019 standard. Until these standards are fully implemented, we transpile your code into ES5, so it can run in current browsers. Wix supports [source maps](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/SourceMap), so that even though the browser runs transpiled ES5 code, you can debug your ES2019 source code in your browser's developer tools.  ## Module support (ECMAScript 2015) Wix supports the native module functionality included in the ES2015 release of JavaScript. To use ES2015 module functionality, you need to follow the [ES2015 Module Syntax](https://developers.google.com/web/shows/ttt/series-2/es2015). Only those items specifically exported in a module are exposed to other files. All other items in your module are internal to the module only. For examples of how to use this functionality, see MDN's [JavaScript modules guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). > **Notes:** > - You can only export functions in files that are located in the Public or Backend sections of your site's code. You can't export functions from page or popup files. > - Certain module export formats are not supported in `events.js`, `data.js`, or `routers.js` files. For more information, see [Module Export Syntax for Backend Events, Data Hooks, and Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md). ### Module scope The following is a list of guidelines that define how you can share modules and functions between, and within, the backend and public scopes: * A JavaScript file or script in backend can import a module from any file in backend or public. * A file in public can import a module from any file in public. * You can import functions from backend and use them in public, using a [web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md). * You can use relative paths to refer to files with the "." prefix. * You can import a module from backend with the _backend/_ prefix. * You can import a module from public with the _public/_ prefix. * Modules can import other modules. ## Wix Fetch [Wix Fetch](http://wix.to/94BuAAs/wix-fetch.html) is an implementation of the standard JavaScript Fetch API, and you work with it the same way, using standard Fetch syntax. You can look at [examples of using Fetch](https://github.com/github/fetch), or check out the [Standard Fetch specification](https://fetch.spec.whatwg.org/). Use Wix Fetch rather than the standard JavaScript Fetch API whenever you need an `http/s` request. You can use Wix Fetch in both backend and frontend code. To use Wix Fetch in your JavaScript code, add `import {fetch} from 'wix-fetch'` to the beginning of your JavaScript file. Among its benefits, Wix Fetch uses promises to handle asynchronous requests, which allows for easier handling of results and errors. ## See also + [Working with promises](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) + [About module export syntax](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Promises ## Article: Working with Promises ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md ## Article Content: # About Promises > **Note:** This article is intended for coders with limited JavaScript experience, and serves as an introduction to this basic JavaScript content. Some of the code your write when developing your site is asynchronous code. Asynchronous code doesn't execute in the order it's written, unlike synchronous code. Instead, it returns a promise. A [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. While the promise is pending, synchronous code continues to run. When developing your website, there are some cases where you need to work with promises. For example: - [Calling backend functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-a-jsw-web-module.md) from your page code. - Calling certain [APIs](https://dev.wix.com/docs/velo.md). Promises are a regular JavaScript concept, so it's easy to use AI chatbots or search engines to troubleshoot. ## Supported IDEs You can handle promises using: - The editor (Wix Studio and Wix Editor) - The Wix IDE (Wix Studio) - You local IDE (Wix Studio and Wix Editor) ## Handle a promise Handling a promise means telling your code to read and use the value of the promise only once its resolved value is known. This means the asynchronous code has finished running. At any given time, a promise has one of the following statuses: - **Pending**: The asynchronous code is still running. - **Fulfilled**/**resolved**: The asynchronous code has finished running and has successfully returned a value. - **Rejected**: The asynchronous code has finished running and has thrown an error. You can tell your code to expect a promise and to wait for the asynchronous code to finish running in the following ways: - [`async`/`await`](#asyncawait): Allows you to treat asynchronous code as if it's synchronous, meaning the next line of code won't run until the promise is resolved. - [`then()`](#then): Allows you to run other synchronous code while your asynchronous code is running. You have more control over when your code runs, but it's less readable. ## `async`/`await` The `async`/`await` syntax allows you to handle promises in a way that makes your asynchronous code appear synchronous. This can make your code more readable, as the next line of code won't run until the promise is resolved. ### Define asynchronous functions Asynchronous functions are functions that contain asynchronous code. An asynchronous function's return value is always a promise. To define a function as asynchronous, add `async` before the function declaration. For example: ```js async function myFunction () {} const myFunction = async () => {} ``` > **Note:** This method is not supported at the top level, because top level code is synchronous. Add `await` before the call to an asynchronous function to make your code wait for the function to resolve before moving on to the next line. For example: ```js const myPromiseValue = await myAsyncFunction(); ``` Then you can use the value of the resolved promise on the next line. For example: ```js async function myFunction () { const myPromiseValue = await myAsyncFunction(); console.log(myPromiseValue); } ``` ### Handle errors with `async`/`await` If your promise is rejected, something has gone wrong in the asynchronous code and you need to handle the error. Use the [`try...catch` block](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) to handle errors. For example: ```js async function myFunction () { try { const myPromiseValue = await myAsyncFunction(); console.log(myPromiseValue); } catch (error) { console.log(error); } } ``` ## `then()` With the `then()` syntax, you can instruct your code to handle promises by running a function once the promise is resolved. For example: ```js asyncFunc().then((resolvedPromiseValue) => { // Do something with resolvedPromiseValue. console.log(resolvedPromiseValue); }) ``` ### Handle `then()` return value If you return a value in the function passed to `then()`, that value is always a promise. For example: ```js const returnValue = asyncFunc().then((resolvedPromiseValue) => { return 'The value of the resolved promise is: ' + resolvedPromiseValue; }) // returnValue is a promise. ``` You can handle this promise in the same way as any other promise: - Use `async`/`await` to use the value synchronously in your code. For example: ```js async function myFunction () { const secondPromiseValue = await firstAsyncFunc() .then((firstPromiseValue) => { return secondAsyncFunc(firstPromiseValue); }) console.log(secondPromiseValue); } ``` - Chain `then()` methods. For example: ```js firstAsyncFunc() .then((firstPromiseValue) => { return secondAsyncFunc(firstPromiseValue); }) .then((secondPromiseValue) => { console.log(secondPromiseValue) }) ``` Both of the above examples log the same value to the console. ### Handle errors with `then()` If your promise is rejected, then something has gone wrong in the asynchronous code and you need to handle the error. Use the [`catch()` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) to handle errors: ```js myAsyncFunction() .then((myPromiseValue) => { console.log(myPromiseValue); }).catch((error) => { console.log(error); }) ``` ## Handle multiple promises simultaneously Waiting for multiple promises to be fulfilled before the execution of some code is supported. You can run multiple asynchronous functions and wait for their promises simultaneously using the [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) method. It accepts multiple promises in an array, and returns an array of resolved values. If any of the promises are rejected, the method throws an error. For example: ```js Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); // Expected output: [ // promise1ResolvedValue, // promise2ResolvedValue, // promise3ResolvedValue // ] }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Module Export Syntax ## Article: Module Export Syntax ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md ## Article Content: # Module Export Syntax Wix supports working in JavaScript to develop websites, as well as [support for modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/java-script-support.md#module-support-ecmascript-2015). This allows you to export modules from your code files and import them into your other code files. This article explains the supported syntax for exporting modules. ## Supported IDEs You can export modules in: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## Supported syntax When exporting functions in the following files, use the standard export syntax contained in the documentation's code examples: + [`events.js`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) + [`data.js`](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md) + [`routers.js`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) + [Service plugin code files](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) You can also use other export formats that are part of the [ES6 (ES2015) module export syntax](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export). However, the following export formats are **not** supported in `events.js`, `data.js`, `routers.js`, or service plugin code files: + **Re-exporting a function:** Directly re-exporting a function imported from an npm or Velo package. For example, this **doesn't** work: ```javascript import { myFunction } from 'myPackage'; export const wixMediaManager_onFileUploaded = myFunction; ``` The following alternative syntax **is** supported: ```javascript import { myFunction } from 'myPackage'; export const wixMediaManager_onFileUploaded = (paramA, paramB) => myFunction(paramA, paramB); ``` + **Dynamic exports:** Exports including a dynamic operation or expression that requires execution to resolve. For example, this **doesn't** work: ```javascript import myPackage from 'myPackage'; export const myRouter_Router = myPackage.router((paramA, paramB) => { // ... }); ``` The following alternative syntax **is** supported: ```javascript import myPackage from 'myPackage'; const functionForExport = myPackage.router((paramA, paramB) => { // ... }) export const myRouter_Router = (paramA, paramB) => functionForExport(paramA, ParamB); ``` + **Exports wrapped in objects:** Exports where functions are defined as properties of an object. For example, this **doesn't** work: ```javascript const dataHooks = { beforeInsert: (paramA, paramB) => { // ... } }; export const MyCollection_beforeInsert = dataHooks.beforeInsert; ``` The following alternative syntax **is** supported: ```javascript const dataHooks = { beforeInsert: (paramA, paramB) => { // ... } }; export const MyCollection_beforeInsert = (paramA, paramB) => dataHooks.beforeInsert(paramA, paramB); ``` + **CommonJS exports:** Exports in the CommonJS (CJS) format that preceded ES6 (ES2015). For example, this **doesn't** work: ```javascript module.exports = { MyCollection_beforeInsert: () => {} }; ``` ## See also + [About JavaScript support](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/java-script-support.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Event Handlers in Wix ## Article: Reacting to User Actions Using Events ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md ## Article Content: # About Event Handlers in Wix Event handlers in Wix work differently than in traditional web development. Normally, you would use `addEventListener()` to add event handlers, selecting DOM elements with methods like `getElementById()` or `querySelector()`. For example, you might write: ```js document.getElementById("myButton").addEventListener("click", function() { console.log("Button clicked!"); }); ``` In Wix, however, you don’t access the DOM directly. Instead, you use the [$w API](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) to interact with elements in the Wix editor and add event handlers, like this: ```js $w('#myButton').onClick(() => { console.log('Button clicked!'); }); ``` ## Supported IDEs You can use event handlers in the following IDEs: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor).
Important: Wix event handlers are now dynamic. While [static event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-static-event-handlers.md) are still supported for existing sites, we recommend using dynamic event handlers for new development and when updating existing code.
## See also + [Add an Event Handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/add-an-event-handler.md). + [Where Do I Put My Code?](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add An Event Handler ## Article: Add An Event Handler ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/add-an-event-handler.md ## Article Content: # Add an Event Handler You can add [dynamic event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) to elements on your site. To add an event handler to an element, you can either: + Write the code for the event handler directly in your page code. + Use the Properties & Events panel in the Wix editor to generate the event handler for you. ## Add an event handler directly in your code Write the event handler in your page code, either outside or inside the page’s [`onReady()`](https://dev.wix.com/docs/velo/api-reference/$w/on-ready.md) function. Access your page code files based on your setup: + **Editor:** Select the Page Code tab in the code panel. + **Local IDE when using [Git integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli/.md):** Locate your site repo's `src` folder. + **[Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio):** Use the `src` folder. Sample event handler: ```js $w('#myButton').onClick((event) => { console.log('Button was clicked!'); $w('#myText').text = 'Hello, World!'; }); ``` You can add or modify event handlers dynamically in your code as needed, for example, `$w('#myElement').myHandlerFunction()`. ## Add an event handler using the Property & Events panel To generate an event handler in the editor using the Properties & Events panel: 1. In the editor, select the element you want to add an event handler to. 2. In the Properties & Events panel, click the event you want to handle. For example, **onClick()** or **onMouseIn()**. 3. The page code will display a new event handler in the code editor: ```js $w('#myButton').onClick((event) => { }); ``` 4. Inside the body of the event handler, add the code you want to execute when the event occurs. For example: ```js $w('#myButton').onClick((event) => { console.log('Button was clicked!'); $w('#myText').text = 'Hello, World!'; }); ``` > **Note:** > > When you add an event handler using the Properties & Events panel, the event handler code is placed outside the `onReady()` function. You can then move the generated code snippet to any location. For a complete list of supported event handlers for each element type, see the [Wix API Reference](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md). ## See also + [About Event Handlers in Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). + [About the Properties & Events Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Static Event Handlers ## Article: About Static Event Handlers ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-static-event-handlers.md ## Article Content: # About Static Event Handlers
Deprecation Notice: This feature is deprecated but will continue to work as expected. Switch to [dynamic event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/add-an-event-handler.md) for your current and future projects. Unlike static event handlers, dynamic event handlers can be added or removed using methods like `$w('#myButton').onClick()`. While static event handlers are still supported for existing sites, we recommend using dynamic event handlers going forward.
A static event handler is predefined and is linked to a specific event. The function can’t be dynamically modified or removed programmatically during execution. **Sample static event handler:** ```js export function myButton_click(event) { console.log('Button clicked'); } ``` ## Migrate from static to dynamic event handlers To migrate a static event handler to a dynamic one: 1. Open the Properties & Events panel for the element with the static event handler. 2. Find the event you want to migrate and click the yellow lightning bolt icon next to the event handler name. 3. Click the **Migrate [eventName] event** link that appears. After migration, your code updates from the old static format to the new dynamic format. For example: **Old format:** ```js export function myButton_click(event) { // Existing code } ``` **New format:** ```js $w('#myButton').onClick((event) => { // Existing code }); ``` ## Delete a static event handler You can no longer use the Properties & Events panel to delete static event handlers. To delete a static event handler, delete the generated code from the page code file. ## See also + [About Event Handlers in Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). + [Add an Event Handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/add-an-event-handler.md). + [About the Properties & Events Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/working-with-the-properties-events-panel.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About CSS Styling ## Article: About CSS Styling ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md ## Article Content: # About CSS Styling CSS allows you to customize the appearance of your website, from colors and fonts to sizes and positions of different elements. You can even add design elements that aren’t yet available in the editor. For example, you can: - Create a blur filter and apply it to any element in your site. - Use mix-blend-mode properties to combine different elements. - Add a gradient color effect to text. ## Supported IDEs You can customize the CSS using: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio). - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio). ## Classes Classes group elements on your site together to easily apply the same styling to all the instances of that group. You can apply custom CSS styling to a global class, semantic class, or custom class. ### Global classes Global classes are built-in and made available to your site. Applying a style to a global class will affect the CSS styling of elements of that class globally, throughout your site. To get started, see the [list of available classes](https://dev.wix.com/docs/velo/api-reference/$w/styling-elements-with-css.md#api-reference_$w_available-classes). ### Semantic classes Most global classes are divided into more specific semantic classes to customize individual aspects. For example, you can apply a custom style to all buttons with `.button` or only apply a style to the label text of a button with `.button__label`. See the [documentation of each element](https://dev.wix.com/docs/velo/api-reference/$w/styling-elements-with-css.md#api-reference_$w_available-classes) for specific information on supported semantic classes. ### Custom classes In addition to the built-in, global classes you can also create your own custom classes and apply CSS styling to just those. For example, you may want to apply a certain animation style to all subscribe buttons throughout your site, but not to all buttons in general. To do so, you can create a custom class called `.button--subscribe` and add custom styling for all elements that you add this class to. ## See also - [Apply custom CSS styling](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/apply-custom-css-styling.md) - For further information on CSS styling options, refer to the [MDN CSS reference docs](https://developer.mozilla.org/en-US/docs/Learn/CSS). - [Wix Studio webinar: Enhance your designs with CSS editing](https://www.youtube.com/watch?v=xynm4m83CqM) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Apply Custom CSS Styling ## Article: Apply Custom CSS Styling ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/apply-custom-css-styling.md ## Article Content: # Apply Custom CSS Styling You can apply custom [CSS styling](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md) to any supported element in your site. ## Step 1 | Add the global.css file The way that you add the `global.css` file depends on the IDE you’re using. ### Editor (Wix Studio) 1. Go to the **Page Code** section of the code panel. 2. In the **CSS** section, click **+ Add global.css**. ### Wix IDE or your local IDE Add a `global.css` file to the `src/styles` folder. ## Step 2 | (Optional) Add a custom class to your site In addition to the [available classes](https://dev.wix.com/docs/velo/api-reference/$w/styling-elements-with-css.md#api-reference_$w_available-classes) you can add a new class to any supported element. You can add new custom classes with code or with the editor: ### Code Add one or more classes to the element with the `add()` function: ```javascript $w("#myElement").customClassList.add("className1", "className2"); ``` ### Editor (Wix Studio) 1. Select an element. 2. In the CSS Classes panel, type your custom class in the Custom classes field: ![custom classes panel in editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d8f840d04075e4169aee11d653dc1dfb.png) ## Step 3 | Apply custom styles to your class In the [`global.css` file](#step-1–add-the-global-css-file) add custom CSS styles to apply. For example: ```css .accordion { text-transform: capitalize; color: yellow; } .button--subscribe { background: linear-gradient(to bottom right, red, yellow); } ``` You can also use [site theme CSS variables](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/site-theme-css-variables.md) to apply your site's theme colors and fonts to your class. For example: ```css .container { background-color: var(--wst-color-fill-background-primary); ``` ## See also - [About CSS styling](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md) - [Webinar: Enhance your designs with CSS editing](https://www.wix.com/studio/academy/webinars/enhance-your-designs-with-css-editing) - For specific examples of available classes, refer to the [API documentation](https://dev.wix.com/docs/velo/api-reference/$w/styling-elements-with-css.md#api-reference_$w_available-classes). - For further information on CSS styling options, refer to the [MDN CSS reference docs](https://developer.mozilla.org/en-US/docs/Learn/CSS). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Site Theme CSS Variables ## Article: Site Theme CSS Variables ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/site-theme-css-variables.md ## Article Content: # Site Theme CSS Variables ## CSS Variable Reference Wix exposes a comprehensive set of CSS variables for each of your site's theme colors and fonts. You can use these variables when applying custom [CSS styling](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/about-css-styling.md) to supported element in your site. These variables represent the site's color and typography styles, enabling dynamic and consistent design across all components. Use this reference to identify and apply the correct variables for colors and fonts in your code. ## Color theme ### General | Style | CSS Variable | |---------------------|------------------------------------------| | Primary background | `--wst-color-fill-background-primary` | | Secondary background| `--wst-color-fill-background-secondary` | | Lines and dividers | `--wst-color-line` | ### Text | Style | CSS Variable | |---------------------|------------------------------------------| | Title | `--wst-color-title` | | Subtitle | `--wst-color-subtitle` | | Body text | `--wst-color-text-primary` | | Secondary text | `--wst-color-text-secondary` | | Links and actions | `--wst-color-action` | | Disabled actions | `--wst-color-disabled` | ### Buttons #### Primary button | Style | CSS Variable | |------------------------------|-----------------------------------------------| | Fill (Regular) | `--wst-button-color-fill-primary` | | Border (Regular) | `--wst-button-color-border-primary` | | Text (Regular) | `--wst-button-color-text-primary` | | Fill (Hover) | `--wst-button-color-fill-primary-hover` | | Border (Hover) | `--wst-button-color-border-primary-hover` | | Text (Hover) | `--wst-button-color-text-primary-hover` | | Fill (Disabled) | `--wst-button-color-fill-primary-disabled` | | Border (Disabled) | `--wst-button-color-border-primary-disabled` | | Text (Disabled) | `--wst-button-color-text-primary-disabled` | #### Secondary button | Style | CSS Variable | |------------------------------|-----------------------------------------------| | Fill (Regular) | `--wst-button-color-fill-secondary` | | Border (Regular) | `--wst-button-color-border-secondary` | | Text (Regular) | `--wst-button-color-text-secondary` | | Fill (Hover) | `--wst-button-color-fill-secondary-hover` | | Border (Hover) | `--wst-button-color-border-secondary-hover` | | Text (Hover) | `--wst-button-color-text-secondary-hover` | | Fill (Disabled) | `--wst-button-color-fill-secondary-disabled` | | Border (Disabled) | `--wst-button-color-border-secondary-disabled`| | Text (Disabled) | `--wst-button-color-text-secondary-disabled` | #### Theme Colors | Style | CSS Variable | |-----------------------|-----------------------------------| | Base 1 | `--wst-color-fill-base-1` | | Base 2 | `--wst-color-fill-base-2` | | Shade 1 | `--wst-color-fill-base-shade-1` | | Shade 2 | `--wst-color-fill-base-shade-2` | | Shade 3 | `--wst-color-fill-base-shade-3` | | Accent 1 | `--wst-color-fill-accent-1` | | Accent 2 | `--wst-color-fill-accent-2` | | Accent 3 | `--wst-color-fill-accent-3` | | Accent 4 | `--wst-color-fill-accent-4` | ## Font theme | Style | CSS Variable | |-------------------------------------|--------------------------------| | Heading 1 titles (reserved for page titles) | `--wst-font-style-h1` | | Heading 2 titles (recommended) | `--wst-font-style-h2` | | Heading 3 titles | `--wst-font-style-h3` | | Heading 4 titles | `--wst-font-style-h4` | | Heading 5 titles | `--wst-font-style-h5` | | Heading 6 titles | `--wst-font-style-h6` | | Large body text (paragraph 1) | `--wst-font-style-body-large` | | Medium body text (paragraph 2; recommended) | `--wst-font-style-body-medium`| | Small body text (paragraph 3) | `--wst-font-style-body-small` | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Dashboard Pages ## Article: Working with Dashboard Pages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/dashboard-admin-pages/about-dashboard-pages.md ## Article Content: # About Dashboard Pages Dashboard pages are pages you create in the editor that only appear on your site’s [dashboard](https://support.wix.com/en/article/wix-dashboard-overview). With dashboard pages, you can create a custom back office allowing you and your [collaborators](https://support.wix.com/en/article/roles-permissions-overview) to efficiently manage your business operations and live database content. Only you and your collaborators can access your dashboard pages. For example, let's say you have a real estate site and have added collaborators to your site. The image below depicts a dashboard page that lists the properties for sale.
You can create several dashboard pages to help you and your collaborators manage your property listings such as: - An upload form for adding new property listings. - A form to simplify making changes to the information about different properties. - A page that lists all of the properties in your live database with search capabilities. - A page that lists all new properties added by form for review before displaying them on your live site. - A page where each collaborator can see the properties they manage. ## Supported IDEs You create dashboard pages in the editor (Wix Studio and Wix Editor). You can view and access any dashboard pages created in the editor when using other IDEs, such as the Local IDE. The IDEs synchronize with the editor to ensure consistency. To add functionality to your dashboard page, add your code to the dashboard's page code file in: - The [Code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md#velo-working-in-the-code-editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) ## Before you begin Before you begin, it's important to note the following: - Dashboard pages appear in your dashboard only after you publish your site. - Site visitors can't access your dashboard pages. However, the underlying page code for a dashboard page is publicly visible. ## Differences between dashboard pages and regular pages Dashboard pages support the same functionality and share most settings with any other page on your site, including the look and feel, with the following exceptions: - They don't have headers or footers. - For sites that include a Members Area, the login bar doesn't appear. - They don't appear in your site menu. - You can't convert a regular page to a dashboard page or a dashboard page to a regular page, but you can convert a dashboard page to a dynamic page. ## Linking to and from dashboard pages When you link to a dashboard page from a regular page or your site's menu, only collaborators are redirected to the dashboard page in your dashboard. All other site visitors are directed to a login page instead. When you link to a regular page from a dashboard page, the regular page opens in your dashboard unless you set the link to open in a new page. ## See also - [Add a Dashboard Page](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/dashboard-admin-pages/add-a-dashboard-page.md) - [wix-dashboard API](https://dev.wix.com/docs/velo/api-reference/wix-dashboard/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add a Dashboard Page ## Article: Add a Dashboard Page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/dashboard-admin-pages/add-a-dashboard-page.md ## Article Content: # Add a Dashboard Page [Dashboard pages](#LINK-PLACEHOLDER) allow you to create a custom back office for you and your [collaborators](https://support.wix.com/en/article/roles-permissions-overview) to efficiently manage your business operations and dynamic content. You can add functionality to your dashboard pages using the [Wix Dashboard API](https://dev.wix.com/docs/velo/api-reference/wix-dashboard/introduction.md), allowing your code to interact with the dashboard environment. > **Note:** In your local IDE, run `wix dev` to open the editor to add a dashboard page to your site. ## Add a dashboard page to your site Dashboard pages can only be added to your site in the editor and can be viewed in your site's dashboard after publishing your site. **Add a dashboard page in Wix Studio** 1. Click the **Pages** ![pages icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/200b9d74a1d550b51058678b2df28b0b.png) icon on the left sidebar. 1. Click the **Add New Page** ![add page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2c36935a6e84dbf551dc5f7301ae6bc3.png) icon and then click **Add** under Dashboard Page.
If the **Introducing Dashboard Pages** panel appears, click **Add Dashboard Page**. 1. Find your new dashboard page in the **Dashboard Pages tab**. 1. Click on the **More Actions** ![show more](https://wixmp-833713b177cebf373f611808.wixmp.com/images/506abdf364c2f86a8f34670cbcac015b.png) icon and then select **Rename** to give your new page a name.
1. Use Wix elements, datasets, and code to build your dashboard pages. 1. Publish your site. 1. Navigate to the dashboard to see your page. **Add a dashboard page in Wix Editor** 1. Click **Add** on the left side of the editor. 1. Click **Page** and then click the add ![add page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9048bd9c78a9635f34d1875af5d678d8.png) icon next to the **Dashboard Page**.
This action opens the **Site Pages and Menu** tab. If the **Introducing Dashboard Pages** panel appears, click **Add Dashboard Page**. 1. In the **Dashboard Pages** panel, give your new page a name.
1. Use Wix elements, datasets, and code to build your dashboard pages. 1. Publish your site. 1. Navigate to the dashboard to see your page. ## Add functionality to your dashboard page Use the [Wix Dashboard API](https://dev.wix.com/docs/velo/api-reference/wix-dashboard/introduction.md) to interact with your dashboard pages, such as displaying toast notifications or navigating users to other pages in the dashboard. To use the Wix Dashboard API, import the relevant functions from `wix-dashboard`: ```js import { } from 'wix-dashboard'; ``` The code example below displays a toast notification at the top of the dashboard page after a product is updated: ```js import { showToast } from "wix-dashboard"; $w.onReady(function () { $w("#updateButton").onClick(() => { const config = { message: "Product updated successfully!", type: "success", }; showToast(config); }); }); ``` ## See also - [About Dashboard Pages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/dashboard-admin-pages/about-dashboard-pages.md) - [wix-dashboard API](https://dev.wix.com/docs/velo/api-reference/wix-dashboard/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Building a Mobile Site with Code ## Article: Writing Code That Only Runs on Mobile Devices ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/mobile/building-a-mobile-site-with-code.md ## Article Content: # About Building a Mobile Site with Code When you build a site, Wix automatically creates a mobile version. Wix resizes your site's content to fit mobile screens and hides any elements from the desktop editor that aren't optimized for mobile. You can customize the mobile version of your site by: - Writing code to change the site’s behavior when a site visitor views it on a mobile device or tablet. For example, you can show text on the desktop when hovering over an element. But this functionality is not available on a mobile device. Instead, you can show the text when tapping. - Managing elements in the [Mobile Editor](https://support.wix.com/en/article/wix-editor-getting-started-with-the-mobile-editor) (Wix Editor) or by using [breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) (Wix Studio). For example, you can hide a button when a visitor opens the site on a mobile device or tablet. This doesn't require coding knowledge. This article only discusses how to manage elements with code. ## Supported IDEs You can write code for mobile using: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## Managing site behavior on different devices with code To write code that only runs on mobile devices, you first need to check what device the code is running on. You do this by wrapping the mobile-only code in a JavaScript conditional statement with the [wix-window-frontend](https://dev.wix.com/docs/velo/apis/wix-window-frontend/introduction.md) API. Your code will look something like this: ```js import wixWindowFrontend from "wix-window-frontend"; if (wixWindowFrontend.formFactor === "Mobile" || ) { // code that will only run on mobile } ``` ## Considerations Consider the following when coding for mobile sites: - You might expect that code written in the code editor while in mobile view would only run when your site is viewed on a mobile device. However, the code actually runs regardless of the type of device on which your site is being viewed. Be sure this code doesn’t display anything unnecessary in desktop view. - Some event handlers, such as `onMouseIn()` or `onMouseOut()` don't work on mobile. You should replace these handlers with `onClick()`. For example: Code for hovering an image in desktop view: ```js import wixWindowFrontend from "wix-window-frontend"; $w.onReady(function () { $w("#image").onMouseIn(async (event) => { await $w("#hiddenText").show("fade"); }); $w("#image").onMouseOut(async (event) => { await $w("#hiddenText").hide("fade"); }); }); //... ``` Code for tapping the same image in mobile view: ```js //... $w.onReady(function () { $w("#image").onClick(async (event) => { if ( (await window.formFactor()) === "Mobile" || (await window.formFactor()) === "Tablet" ) { if ($w("#hiddenText").hidden) { $w("#hiddenText").show("fade"); } else { $w("#hiddenText").hide("fade"); } } }); }); ``` ## See also - [Build a Mobile Site with Code](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/mobile/build-a-mobile-site-with-code.md) - [Mobile Editor](https://support.wix.com/en/article/wix-editor-getting-started-with-the-mobile-editor) - [Breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working in the Mobile Editor ## Article: Working in the Mobile Editor ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/mobile/working-in-the-mobile-editor.md ## Article Content: # Velo: Working in the Mobile Editor The primary functionality for the mobile Editor (Wix Editor) or [breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) (Wix Studio) is to design the layout of your site when viewed on a device such as a tablet or mobile device. ### Site behavior on different devices You can use code to control how your site behaves on different devices. To find out which device your site is being displayed on, use the `formFactor` property of the [`wix-window-frontend`](https://www.wix.com/velo/reference/wix-window-frontend/formfactor) API, which returns either "Desktop", "Mobile", or "Tablet". You can then add code to your site that includes a condition to control how your elements behave, based on the device. ### Hiding elements on different devices You can hide certain elements on your site when displayed on different devices using the mobile Editor (Wix Editor) or breakpoints (Wix Studio). Unlike an element's `hidden` property, which controls the visibility of an element when a page first loads, hiding elements allows you to specify which elements should never appear on tablet or mobile views of your site. > **Note:** > Breakpoints (Wix Studio) allow you to hide elements in customized breakpoint views for your site in addition to tablet and mobile views. In the UI of the mobile Editor (Wix Editor) you can hide elements only in the mobile view of your site. **Wix Editor:** - To hide an element from the mobile version of your site, navigate to the mobile Editor. Click on your element, then click the **Hide Element** button ![Hide mobile editor element](https://wixmp-833713b177cebf373f611808.wixmp.com/images/23b5406a0d35445bf7086edb7524ac0a.png). - To unhide the element, navigate to the **Hidden on Mobile** panel on the left and click **Show** on the element you wish to unhide. ![Hidden on Mobile](https://wixmp-833713b177cebf373f611808.wixmp.com/images/51a8ef713be7475b82fdf5b967e5047b.png) **Wix Studio:** - To hide an element from different breakpoints of your site, navigate to the breakpoint of choice and click on your element. Click on the **More Actions** button ![More Actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1617580fa1f44c7a618a2ffb45634efc.png), then click **Hide**. ![Hide mobile studio element](https://wixmp-833713b177cebf373f611808.wixmp.com/images/40e0d84e31d9833b1d1a8b6506f8113d.png) - To unhide the element, navigate to the **Layers** panel on the left. Look for the element you want to unhide, click the **Show** button. ![Unhide mobile studio element](https://wixmp-833713b177cebf373f611808.wixmp.com/images/60ecbdfa31e995f6d6720fdd79d57947.png) > **Note:** > - Changes made in larger [breakpoints cascade](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints#cascading-design) down to smaller breakpoint sizes. For example, if you hide the element in the tablet breakpoint, the element will automatically be hidden in the mobile breakpoint. You can override the cascading effect by unhiding the element in the mobile breakpoint.
**Important:** Hiding an element using the mobile Editor (Wix Editor) or breakpoints (Wix studio) removes that element from the page's DOM. Any code that relies on the hidden element's existence will break.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Redirecting Mobile Visitors ## Article: Redirecting Mobile Visitors ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/mobile/redirecting-mobile-visitors.md ## Article Content: # Velo Tutorial: Redirecting Mobile Visitors
Visit the Velo by Wix website to onboard and continue learning.
You can use Velo to redirect visitors to mobile-friendly pages in your site. The basic steps to redirect to mobile pages are: 1. Use code to check if your site is being viewed on a mobile device or a desktop device 2. If your site is being viewed on a mobile device, use code to redirect to another page This tutorial has 2 parts: * Instructions on how to get set up, including code you can copy and paste onto your page * An explanation of what each line of code does ### Instructions 1. Go to your site's home page. 2. Copy the code below and paste it in your page code. 3. Make sure to substitute your mobile home page URL in line 9 where it says 'http://www.myMobileHome.com.' 4. Publish the page to see the redirect in action. (Previewing isn't enough.) ```javascript import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { setTimeout(function () { if(wixWindowFrontend.formFactor === "Mobile"){ wixLocationFrontend.to('http://www.myMobileHome.com'); } }, 7500); }); ``` ### Understanding the Code The code checks the kind of device. If mobile, the code redirects the visitor to a mobile-friendly home page. Import statements on lines 2 and 3 bring in the APIs we need: * [`wixWindowFrontend.formFactor`](https://www.wix.com/velo/reference/wix-window.html#formFactor) checks the device type. * [`wixLocationFrontend.to`](https://www.wix.com/velo/reference/wix-location.html#to) redirects the visitor to the mobile page's URL. ```javascript // Import statements for the Wix Location Frontend and Window APIs import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; ``` The redirect occurs after a time delay. This gives the visitor time to see notices on the page you are redirecting from. Line 4 contains the setTimeout function that creates this delay. ```javascript $w.onReady(function () { // Adds a time delay so the visitor anticipates the redirect setTimeout(function () { ... ... ... // The timeout value is 7500 nanoseconds }, 7500); }); ``` With an **if** statement on line 10, we check the type of device using `wixWindowFrontend.formFactor`. ```javascript import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { // Adds a time delay so the visitor anticipates the redirect setTimeout(function () { // Checks what kind of device if(wixWindowFrontend.formFactor === "Mobile"){ // If mobile, we redirect to a mobile-friendly page wixLocationFrontend.to('http://www.myMobileHome.com'); } // The timeout value is 7500 milliseconds }, 7500); }); ``` On line 13, we use `wixLocationFrontend.to` to perform the redirect. ### API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). * [wix-window-frontend](https://www.wix.com/velo/reference/wix-window.html) * [wix-location-frontend](https://www.wix.com/velo/reference/wix-location.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Mobile Site with Code ## Article: Displaying Elements in Mobile Only ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/mobile/build-a-mobile-site-with-code.md ## Article Content: # Velo Tutorial: Displaying Elements in Mobile Only You can use Velo to control elements so they display only in the mobile version of your site. The basic steps to set up an element to display only in mobile are: 1. Set the element to be hidden when the page loads. 2. Use code to check if your site is being viewed on a mobile device or a desktop device. 3. If your site is being viewed on a mobile device, use code to display the hidden element. ### Tutorial #### To set up an element to display only in mobile: 1. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** [Enable Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site) for your site. 2. Add the element that you want to display only in mobile. 3. With the element selected, select the **Hidden** checkbox in the Properties & Events panel. This sets up the element so that when your page loads this element isn't displayed. Make a note of the **ID** for your element since we'll need to use that a little later.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/25/70758b0c-cfdb-4fc5-a2d9-a3ca3201ac0b/1e6c89b8-6597-4c08-8e26-3730f70904c2.png)
4. Now we need to check whether your site is being viewed on a mobile device. Open the code editor. You'll see that following is already written. ```javascript $w.onReady(function () { // Write your JavaScript here // To select an element by ID use: $w("#elementID") // Click "Preview" to run your code }); ``` 5. Add a line above the first line and enter the following code. This lets your site work with the `wix-window-frontend` module, which lets you determine the type of device your site is being viewed on. ```javascript import wixWindowFrontend from 'wix-window-frontend'; ``` 6. Replace lines 2-6 with the following code. This checks the type of device your site is being viewed on. It then sets the element that you set to be **Hidden** to be displayed for mobile only.  ```javascript if(wixWindowFrontend.formFactor === "Mobile"){ $w("#button8").show(); } ``` 7. To make this code work with your element, replace **button8** with the ID for your element that you noted in step 3. 8. When you are done, your code should look like this: ```javascript import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { if(wixWindowFrontend.formFactor === "Mobile"){ $w("#your_element's_ID").show(); } }); ``` 9. If you have more than one element that you want to display only in mobile:  1. Complete step 3 for each element. 2. Add a line after line 5 in the example above. 3. Copy line 5 and paste it into the new line. 4. Replace the ID of the first element with the ID of the second element. Now your code should look like this: ```javascript import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { if(wixWindowFrontend.formFactor === "Mobile"){ $w("#your_first_element's_ID").show(); $w("#your_second_element's_ID").show(); } }); ``` ### API List The following API is used in the code in this article. To learn more, see the [API Reference.](https://www.wix.com/velo/reference/) * [wix-window-frontend](https://www.wix.com/velo/reference/wix-window) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Page Rendering ## Article: About the Page Rendering Process ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md ## Article Content: # About Page Rendering Page rendering is when your site's code is converted to a visual layout that's displayed in the browser. It occurs when visitors first enter your site, and for each subsequent page load, as they access different pages within the same site. To enhance performance, Wix render's your site code on the server and in the browser. You can use the [Rendering API](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/rendering/introduction.md) to control where your code runs in the rendering process. ## Supported IDEs You can implement the Rendering API using: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor) ## Rendering process When a page request is made to the server, the server executes server-side rendering and once completed returns the HTML and all the necessary data to render the page. Once the browser gets the server response it displays the HTML immediately so the page is fully visible, though not yet responsive. Then, the browser runs the same page code again, adding the interactive actions. This double rendering occurs on initial site load, first on the server and then in the browser. Navigating to different pages on the same site, however, typically renders only once in the browser for each page load. As a result of the double rendering, any code in your `onReady()` function may often execute twice, to provide faster initial loading time to access a site. ## Side effects Because your `onReady()` runs twice during double rendering, it may produce side effects. For example, if your code inserts an item into a collection, that insertion occurs twice, unless you explicitly add code to prevent that from happening. To avoid such unwanted side effects, use the [Rendering API](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/rendering/introduction.md) to conditionally control what code runs on the server or browser. You can also use [warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-warmup-data.md) to further optimize page loading. ## Test your code Keep the following in mind when testing code that uses Rendering API. - Rendering never occurs in the server when [previewing](https://support.wix.com/en/article/studio-editor-saving-previewing-and-publishing-your-site#previewing-a-site) your site. - You can't see logs produced by `console.log()` calls in the [Developer Console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) or in your browser's [Developer Tools](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) when they are performed on the server. ## See also - [About warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-warmup-data.md) - [Optimize page rendering with warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/optimize-page-rendering-with-warmup-data.md) - [Webinar: Planning for scale: large data design considerations](https://youtu.be/BGnpQ_LTHPg?feature=shared&t=1464) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Warmup Data ## Article: About Warmup Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-warmup-data.md ## Article Content: # About Warmup Data Warmup data is used to optimize [page rendering](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md) when your site's pages are rendered on both the server and the client, ensuring that costly data operations are done only once. Generally, the server executes data operations faster than the browser. With warmup data, you can pass the results of your server operations to your client-side code. During client-side rendering, you can write code to check if those operations were already done and set to warmup data, and if the warmup data exists, use it in your code. This prevents the client from performing the same expensive operations again. Use the [Rendering API](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/rendering/introduction.md) to check whether your code is being run on the client or the server, and the [Warmup Data API](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/warmup-data/introduction.md) to set and get the warmup data. ## Supported IDEs You can optimize page rendering with `warmupData` using: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor) ## Client-side rendering When working with warmup data it's important to keep in mind that there may be cases where client-side rendering happens before server-side rendering. In such cases, you can't rely on the warmup data being set on the server, and need to execute those operations directly on the client-side. Note that you can only perform the following operations on the client-side: + Accessing the browser's storage  + Using 3rd-party analytics + Creating dynamic event handlers  + Retrieving information about the client's browser + Navigating to another page ## See also - [About page rendering](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md) - [Optimize page rendering with warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/optimize-page-rendering-with-warmup-data.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Optimize Page Rendering with Warmup Data ## Article: Optimize Page Rendering with Warmup Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/optimize-page-rendering-with-warmup-data.md ## Article Content: # Optimize Page Rendering with Warmup Data Use [warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-warmup-data.md) to speed up your site's [page rendering](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md) by reducing the number of data operations that occur during client-side rendering. Follow this procedure to make a data query that is called only once during server-side rendering. The query results are sent as warmup data for display during client-side rendering. If the data query was not made during server-side rendering, it is called for the first time during client-side rendering. 1. Add your import statements. ```javascript import wixData from 'wix-data'; import wixWindowFrontend from 'wix-window-frontend'; ``` 2. Define a `getData()` function that makes a `wixData` query. Use [`warmupData.set`](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/warmup-data/set.md) to conditionally set the query results as warmup data if the query is being made during server-side rendering. ```javascript async function getData() { const results = await wixData.query("myCollection") .find(); if (wixWindowFrontend.rendering.env == "backend") { wixWindowFrontend.warmupData.set("myWarmupData", results.items); } return results; } ``` 3. In your `onReady()` code, store your data in a variable called `dataResults`. Retrieve the warmup data by calling `warmupData.get`. If server-side rendering does not occur, `dataResults` should default to the return value of `getData()`. ```javascript $w.onReady(async function () { const dataResults = wixWindowFrontend.warmupData.get("myWarmupData") || await getData(); // Use your data as needed. }); ``` In this example, the `dataResults` variable is populated with the results of the query in all cases. When possible, we have optimized performance by using the warmup data initially fetched on the server, instead of running the query a second time during client-side rendering. The full page code looks like this: ```javascript async function getData() { const results = await wixData.query("myCollection") .find(); if (wixWindowFrontend.rendering.env == "backend") { wixWindowFrontend.warmupData.set("myWarmupData", results.items); } return results; } $w.onReady(async function () { const dataResults = wixWindowFrontend.warmupData.get("myWarmupData") || await getData(); // Use your data as needed. }); ``` ## See also - [About page rendering](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md) - [About warmup data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-warmup-data.md) - [Warmup Data API](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/warmup-data/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the URL Structure of Premium and Free Sites ## Article: About the URL Structure of Premium and Free Sites ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/urls/about-the-url-structure-of-premium-and-free-sites.md ## Article Content: # About the URL Structure of Site Pages Understanding the URL structure of Wix site pages is crucial for tasks such as routing, linking, SEO, and query parameter manipulation. ## Sample URLs The structure of the URLs differ between Wix premium sites and Wix free sites: - **Premium sites**: Use a custom domain, such as `domain.com`: `https://domain.com/animals/mammals/elephant?species=african-elephant`. - **Free sites**: Reflect the Wix editor as the domain followed by the site name, such as `user.wixstudio.io/zoo` for Wix Studio sites and `user.wixsite.com/zoo` for Wix Editor sites. - **Wix Studio**: `https://user.wixstudio.io/zoo/animals/mammals/elephant?species=african-elephant` - **Wix Editor**: `https://user.wixsite.com/zoo/animals/mammals/elephant?species=african-elephant` You can get the full site URL, as well as its parts, using the [Wix Location Frontend API](https://dev.wix.com/docs/velo/api-reference/wix-location-frontend/url.md). ## Key parts of a URL | Term | Description | Example | |-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------| | **Protocol**| Standard part of the URL that indicates the protocol used to access the resource. | `https://` | | **Base URL**| Represents the root domain of your site. Premium sites use a custom domain, while free Wix sites include the editor followed by the site name. | Premium: `domain.com`
Free (Wix Studio): `user.wixstudio.io`
Free (Wix Editor): `user.wixsite.com` | | **Prefix** | Component used for dynamic paths and router pages. Prefixes are useful for [making Dynamic Page URLs](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/making-dynamic-page-urls-meaningful-with-prefixes.md), or for determining which incoming requests the [router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) handles. | `/animals` | | **Path** | The path typically follows the base URL or the prefix, if a prefix exists. For dynamic pages and router cases, paths are used for structure or to encode information to indicate what dynamic content to show. | `/mammals/elephant` | | **Query** | Query parameters are key-value pairs appended to the URL to pass additional data. | `?species=african-elephant` | ## See also - [Wix Location Frontend API](https://dev.wix.com/docs/velo/api-reference/wix-location-frontend/url.md) - [Making Dynamic Page URLs](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/making-dynamic-page-urls-meaningful-with-prefixes.md) - [Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Site Backend ## Article: About the Site Backend ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/about-the-site-backend.md ## Article Content: # About the Site Backend When you build a site on the Wix Dev Platform, you get access to a zero-setup, serverless backend environment. Experience lightning-fast performance with our powerful Node.js server-side runtime, designed for seamless initialization and code execution. Scale and mobilize your backend operations securely, reliably, and consistently. Here is some of the functionality you can implement in your site's backend: + Interface with [Wix APIs](https://dev.wix.com/docs/velo.md) to execute workflows and [handle backend events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md). + Store and manage your data with Wix's out of the box [database](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md), and the [`wix-data`](https://dev.wix.com/docs/velo/api-reference/wix-data/introduction.md) API, or [integrate with an external database](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md). + Seamlessly export backend functions to your site's frontend using [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md). + Expose REST endpoints from your site's backend using [HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/introduction.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Web Modules ## Article: About Web Modules ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md ## Article Content: # About Web Modules Web modules are backend files with a `.web.js` extension. They allow you to write backend functions that you can easily [call from the frontend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md). Use web modules for calling backend code from the frontend: + To protect code that may expose sensitive data, or business logic, if it runs in the frontend. + To centralize and contain business logic such as 3rd-party API calls and database querying. + To avoid [cross-origin resource sharing (CORS)](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) restrictions that can occur when calling 3rd-party APIs from the frontend. Web modules cannot be used to export primitive values or objects that you want to import on the frontend. You can create web modules using: - The Code panel in the editor (Wix Studio and Wix Editor) - Wix IDE (Wix Studio) - Your local IDE (Wix Studio and Wix Editor). ## Web methods To expose a function within a web module so that it can be called from the frontend, you need to wrap it in a [web method](https://dev.wix.com/docs/velo/apis/wix-web-module/introduction.md). Web methods provide the mechanism that allows your function to be called asynchronously from the frontend and adds a [permissions](#permissions) check on the caller of your function. Here is what a web method looks like when defined in the backend and called from the frontend: ```js // In a file with the .web.js extension import { Permissions, webMethod } from "wix-web-module"; export const myFunction = webMethod( Permissions.Anyone, () => { // Function logic }); ``` ```js // In frontend code import { myFunction } from "backend/someFile.web"; // ... const fromBackend = await myFunction(); ```
Advanced: How web methods work When you import a web method on the frontend, you get a proxy function to the web method. This proxy uses an XMLHttpRequest to invoke the function in the backend. The runtime listens to those invocations and calls the appropriate function. The arguments and return value are serialized and deserialized using JSON.
View a demo: `youtube:https://www.youtube.com/watch?v=qRw0atYFY4I&list=PLi3fKZF2b29g5BQJoYV1sK0nrvM1mu3vm&index=4` ## Permissions Because web modules expose your site's backend functionality, it's important to restrict who can call web methods from the frontend. Do this by setting the permissions for each web method to be as restrictive as possible. It is especially important to set restrictive permissions when: - Passing sensitive data from the backend to the frontend. - Exporting functions that require [`elevation`](https://dev.wix.com/docs/velo/apis/wix-auth/elevate.md). The permissions options are: - **Anyone:** The function can be called from the frontend on behalf of any site visitor. - **Site Member:** The function can be called from the frontend on behalf of logged-in site members. - **Admin:** The function can be called from the frontend on behalf of logged-in site owners and collaborators. ## Debugging web modules You can debug the code in web modules as you would [debug any backend code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md). Additionally, you can use `console.log()` in your web methods and the logs will appear in the Developer Console when previewing your site. For security reasons, the logs will not appear in your browser's console when previewing or on your published site. ## Deprecated `.jsw` web modules Web modules were originally created using `.jsw` files. This method for creating web modules is now deprecated. However, existing [`.jsw` web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-a-jsw-web-module.md) will continue to work as expected. ## See also [Call backend code from the frontend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Call Backend Code from the Frontend ## Article: Calling Backend Code from the Frontend ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md ## Article Content: # Call Backend Code from the Frontend [Web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) allow you to write backend functions that you can easily call from the frontend. The following describes how to create, export, and call web module functions, also known as web methods. ## Step 1 | Add a web module The way you add a web module file depends on which IDE you're using. ### Editor 1. Navigate to **Public & Backend** in the code panel. 2. Hover over the **Backend** heading, click the plus icon ![plus icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e45800424595befdc6fe5931a1b7c0b4), and then select **Add web module**. This action creates a web module file with a `.web.js` extension. ![Add web module](https://wixmp-833713b177cebf373f611808.wixmp.com/images/721daee653bbb56634e4ca349f8597c0) ### Wix IDE or your local IDE Create a new file in the `backend-modules` directory and name it with the `.web.js` extension. ![alt text](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1675cc7cb365bbea2f3d57bd5bf93b2b) ## Step 2 | Define a web method in the backend Add an exported [web method](https://www.wix.com/velo/reference/wix-web-module/webmethod) in the file you created above. The web method wraps an inner function that contains the logic you want to call from the frontend. To create a web method, call the `webMethod()` function and define the web method's permissions and its inner function: 1. Add the necessary imports: ```js import { Permissions, webMethod } from 'wix-web-module'; ``` 2. Call the `webMethod()` function and store its returned value in an exported variable. ```js export const myFunction = webMethod(); ``` 3. Pass the `webMethod()` function a [permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) value as the first argument: ```js export const myFunction = webMethod(Permissions.SiteMember); ``` 4. Pass the `webMethod()` function an inner function as the second argument: ```javascript export const myFunction = webMethod( Permissions.SiteMember, (someParam) => { // Some functionality for site members to call from the frontend return `You passed me ${someParam}`; } ); ``` ## Step 3 | Call the function from the frontend To call a web method from the frontend: 1. Import the exported web method from the web module you created above: ```javascript import { myFunction } from 'backend/weather.web'; ``` 2. Call the imported function: ```js const fromBackend = await myFunction(someValue); ``` Remember, web module functions are always asynchronous. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Call Backend Code from a jsw Web Module ## Article: Call Backend Code from a jsw Web Module ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-a-jsw-web-module.md ## Article Content: # Deprecated: Call Backend Code from the Frontend Using a jsw Web Module [Web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) allow you to write backend functions that you can easily call from the frontend.
**Warning:** Web modules defined using `.jsw` files are deprecated and you will soon be unable to create new `.jsw` files. However, existing `.jsw` web modules will continue work as expected. Switch to [web module defined using `.web.js` files](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) for your current and future projects.
The following describes how to create, export, and call web module functions. ## Step 1 | Define a function in the backend Add an exported function to an existing `.jsw` file. ```js export myFunction(someParam) { // Some functionality to call from the frontend return `You passed me ${someParam}`; } ``` ## Step 2 | Set permissions Set [permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) for each function in the web module if necessary. The default permission is **Anyone**. You can set permissions in the Wix Editor or by editing the permissions.json file, depending on your development environment: ::::tabs :::Wix-Editor To set permissions for a web module function: 1. Hover over your web module, click the more actions menu, and select **Edit Web Module Permissions**. \ The web module permissions panel opens and displays each exported function in your web module. 1. Click the dropdown next to a function name to define its permissions. ::: :::Wix-and-Local-IDE When using the Wix IDE or a local IDE with GitHub integration, you can add and edit permissions using the `permissions.json` file. The `permissions.json` file is located in the `src/backend` folder. The file contains a `"web-methods"` object where each key represents a web module file using the syntax `backend/{path-to-file}/.jsw`. Under each file key, you define permission objects for each exported function. The `"web-methods"` object must also contain a `"*"` key that defines default permissions for any functions not explicitly configured. Set permissions using the following values: * **Owner-only access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `false` * `anonymous.invoke` : `false` * **Site member access**: * `siteOwner.invoke`: `true` * `siteMember.invoke`: `true` * `anonymous.invoke` : `false` * **Anyone can access**: * `siteOwner.invoke`: `true` * `siteMember.invoke` : `true` * `anonymous.invoke`: `true` Here is an example with 3 functions from `helperFunctions.jsw`, each having different permission levels: ```json // permissions.json file { "web-methods": { // Default web method. "*": { "*": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } } }, // Web method for the web module. "backend/helperFunctions.jsw": { "calculate": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } }, "fetchData": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": false }, "anonymous": { "invoke": false } }, "syncWithServer": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": false } } } } } ``` ::: :::: ## Step 3 | Call the function in the frontend To call a web method from the frontend: 1. Import the exported web method from the web module you created above: ```javascript import { myFunction } from 'backend/weather.web'; ``` 2. Call the imported function: ```js const fromBackend = await myFunction(someValue); ``` Remember, web module functions are always asynchronous. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Backend Events ## Article: Velo Backend Events ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md ## Article Content: # About Backend Events Many [Velo APIs](https://dev.wix.com/docs/velo.md) include events that trigger when the required conditions are met. Unlike in the frontend, where event handlers are defined in the page code, for backend events, handlers are defined in the `events.js` backend file. > **Note**: When developing websites, you can only handle events using Velo. Handling site events using the JavaScript SDK [isn't currently supported](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md#velo-apis-you-should-continue-to-use). For example, you might have backend event handlers that trigger when: + An invoice is paid. + A file has been uploaded to the Media Manager. + A form is submitted by a site visitor. Backend events are only triggered for published sites and don't work in preview mode. ## Supported IDEs You can define backend event handlers using: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor) ## Velo Package Backend Events [Velo packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) are code libraries built with Velo that allow you to add specific functionality to your site, saving you the time you would have spent coding the functionality on your own. Some Velo packages include backend events, which are contained in their own `events.js` files. So if you add a Velo package that includes backend events, you might end up with multiple `events.js` files in several locations. ## Testing and Debugging To test an event handler in preview mode, use [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md). Functional testing saves you time and effort when building your own system for triggering and testing your backend functions, by allowing you to quickly test backend functions directly in the code editor. See step-by-step instructions on [how to test your backend code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md). ## See Also + [Define backend event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/define-backend-event-handlers.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Define Backend Event Handlers ## Article: Define Backend Event Handlers ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/define-backend-event-handlers.md ## Article Content: # Define Backend Event Handlers You can define [backend event](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) handlers in the `events.js` backend file. > **Note**: When developing websites, you can only handle events using Velo. Handling site events using the JavaScript SDK [isn't currently supported](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md#velo-apis-you-should-continue-to-use). ## Step 1 | Add an `events.js` backend file The way that you add an `events.js` file to the backend depends on which IDE you're using. ### Editor 1. Hover over **Backend** in the **Public & Backend** section of the code sidebar. 2. Click the **More Actions** icon ![plus icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e45800424595befdc6fe5931a1b7c0b4.png) and then click **Handle backend events**. ![Add events.js](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a1fac75ac6a2e949a10e8098f84d04ea.png) ### Wix IDE or your local IDE Add an `events.js` file to the `src/backend` folder. ## Step 2 | Define a backend event handler To define an event handler in the `events.js` file, export a function using a [supported export syntax](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md). The function name should consist of the Wix module and the name of the event, separated by an underscore. Check the **Events** subcategory in most APIs in the [API documentation](https://dev.wix.com/docs/velo.md) to confirm the exact function naming and to see what information is passed to the event handler when it is called. Here is an example of an event handler that [triggers when an invoice is paid](https://dev.wix.com/docs/velo/apis/wix-billing-backend/events/on-invoice-paid.md): ```javascript export function wixBilling_onInvoicePaid(event) { const invoiceId = event.id.id; const email = event.customer.email; // Add your own logic here. } ``` ## Step 3 | Test and debug your function There are two ways to [test your backend event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md#testing-and-debugging): + In preview mode, use [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) to call your event handler function manually. + Publish your site, then trigger the event that activates your event handler. Watch a demo of how to set up backend event handlers: `youtube:https://youtu.be/-LAtdo0mbiY?t=162` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Service Plugins ## Article: Custom App Extensions Using SPIs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md ## Article Content: # About Service Plugins
**Important:** Some service plugins are in Beta. This means that some aren't yet available to all users, and are subject to change. They also aren't available yet in the SDK.
Service plugins provide a powerful way to expand what your Wix site can do. You can extend and customize using plugins by: - Injecting your own custom logic into an existing, out-of-the-box app flow. Depending on the plugin, you can address certain aspects of a flow or add a new stage in a flow. - Integrating 3rd-party services into app flows on a site. Service plugins let you customize specific parts of flows in apps created by Wix without rebuilding entire features. For example, instead of creating a custom checkout page using the [Checkout API](https://www.wix.com/velo/reference/wix-ecom-backend/checkout), you can use service plugins to inject custom logic at specific points in the existing flow. ## Examples of service plugins **Custom shipping providers:** When you set up a Wix Store, there are a limited number of payment and shipping rates service providers to choose from. If you want to use a provider not currently on the list, you can create a service plugin that allows you to add more shipping rate providers to your store's checkout flow. This type of service plugin involves integrating with a 3rd-party service provider. **Additional checkout fees:** The default eCommerce checkout process doesn't allow you to add additional fees that aren't related to specific line items. You can use a service plugin to add extra fees, like charges for gift wrap or fragile items, to your checkout flow. This type of service plugin involves injecting your own logic into an existing flow in an app created by Wix. ## How service plugins work Service plugins fulfill a kind of contract between your code and existing app flows. You, or a 3rd-party service provider, commit to providing data in a certain format, and the app that manages the flow commits to using this data in its flows at specific points. Each service plugin consists of a set of backend code files that utilize API methods for implementation. Apps created by Wix call the methods automatically at specific points in their flows. The methods receive and return data in a specific format. The returned data is then integrated by the app created by Wix into its flow. The [reference documentation](https://www.wix.com/velo/reference/spis/getting-started) for each method indicates at what point in a flow an app triggers the method. The documentation also outlines the data that must be provided as arguments to the method when it's triggered, and the format of the data that the method must return. Here's how it works: 1. At certain points, an app's activity on your Wix site triggers a call to a service. 2. When triggered, the Wix platform passes the information in the request to the provider. The provider needs this information to provide the service to the Wix platform. 3. Wix processes the request for the service according to the implementation defined in the service plugin files in the backend. 4. The service provider returns the needed information and the Wix platform consumes and/or displays the information. For example: 1. Whenever a customer checks out a cart, the Wix eCommerce app calls the `calculateAdditionalFees()` method. 2. The service provider receives the parameters that it needs from Wix eCommerce, such as the cart and line items. 3. The service provider then returns data about additional fees in a specific structure. The app uses the data received from the method response to display additional fees in a customer's checkout flow. You can add any custom logic to calculate the additional fees as long as the method returns your data in the proper structure. ## Legal notices If you connect to a 3rd-party provider using service plugins, you agree to the [Wix.com Terms of Use](https://www.wix.com/about/terms-of-use). Wix is not responsible for your use of such a 3rd-party provider, and any liability resulting from such use will be your responsibility. ## When to use service plugins vs APIs Without service plugins, if you want to change your out-of-the-box app flow just a little, you might have to rewrite large parts of the flow, such as creating a custom checkout page using the [Wix eCommerce Checkout API](https://www.wix.com/velo/reference/wix-ecom-backend/checkout). This could involve writing many lines of code and creating custom collections. Because service plugins only affect a small part of an app's flow, service plugin methods require fewer lines of code and less maintenance than when working with APIs. So wherever possible, you want to use service plugin methods. For more information, read [Understanding the difference between service plugins and APIs](https://medium.com/wix-engineering/let-others-solve-your-problems-change-your-mindset-from-apis-to-spis-781afa48574f). ## Terminology | Term | Description | | ---- | ----------- | | Service plugins (formerly SPIs and custom extensions) | A feature that lets you extend the services provided on a site using code. You can make these plugins by adding your own custom logic into a flow for an app created by Wix, or by adding data received from a 3rd-party service provider into a flow for an app created by Wix. You implement service plugins by adding files to backend code files to a site. These files contain code for your custom logic method calls that trigger at specific points in the flow for the app created by Wix. These custom logic method calls use a type of API that defines a service, but leaves the implementation of that service to service providers. | | Service provider | The entity providing a service, for example, a shipping company. The service is either a custom service or a 3rd-party service. | | Wix user | You, the Wix user or collaborator responsible for developing the code needed for the service plugin. Your code uses your own custom logic or accesses a service provided by a 3rd-party service provider. | | Service | Any additional functionality you add to the site that's not part of the original flow in an app created by Wix. The Wix user codes the interface to the service with the service plugin feature. The Wix user or a 3rd-party can write the service. If a 3rd-party provides the service, the Wix user writes the code for the interface by [accessing 3rd-party APIs](https://support.wix.com/en/article/velo-accessing-3rd-party-services-with-the-fetch-api) with [wix-fetch](https://www.wix.com/velo/reference/wix-fetch), and/or using [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md). | | App created by Wix | The app created by Wix whose functionality you extend. For example, Wix eCommerce has several Service Plugins available for customizing its flows. | ## See also - [Implement service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/implement-service-plugins.md) - [Understanding the difference between service plugins and APIs](https://medium.com/wix-engineering/let-others-solve-your-problems-change-your-mindset-from-apis-to-spis-781afa48574f) - [Available service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/available-service-plugins.md) - [Service plugins reference documentation](https://www.wix.com/velo/reference/spis/getting-started) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Implement Service Plugins ## Article: Implementing Service Plugins ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/implement-service-plugins.md ## Article Content: # Implement Service Plugins Implement [service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) to extend the functionality of apps created by Wix on a site. > **Note:** You currently can't add the service plugin feature to a site when using Git Integration & CLI, or using the SDK. The process of implementing a service plugin has 5 steps: 1. [Creating a service plugin on a site](#step-1--create-a-service-plugin-on-a-site) 2. [Implementing and testing your custom code](#step-2--implement-your-service-plugin-with-custom-code) 3. [Testing the service plugin](#step-3--test-the-service-plugin) 4. [Deploying the service plugin](#step-4--deploy-the-extension) 5. [Removing an extension](#step-5--optional-remove-an-extension) The end result is a functional service plugin that extends a site's capabilities at specific points in app flows. For detailed instructions, see the tutorial for each [available service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/available-service-plugins.md). ## Step 1 | Create a service plugin on a site This step adds a service plugin to a site and creates the necessary folder structure. At the end of this step, you'll have a service plugin folder with template files ready for customization. To create a service plugin on a site: 1. If necessary, add the relevant app to a site, such as Wix Stores. 2. Go to the **Public & Backend** section of the **Code** sidebar. 3. Hover over **Service Plugins**, and click the **Add** icon to add the integration that you want. 4. Follow the prompts to add the plugin, and accept any terms and conditions that display. 5. Enter a name for your integration, and click **Add & Edit Code**. The name can't contain spaces or special characters. > **Note:** If you're using the Wix IDE, your plugin appears in the `/src/backend/service-plugin/` folder. If your service plugin doesn't appear, try refreshing both the editor and the Wix IDE. ## Step 2 | Implement your service plugin with custom code This step involves writing the custom logic for your service plugin in the generated template files. At the end of this step, you'll have functional code that implements your service plugin's logic. To implement your service plugin with custom code: 1. Locate the folder created in the previous step under **Service Plugins** in the **Public & Backend** section of the **Code** sidebar. The folder name is based on the plugin you chose and contains 2 files: `-config.js` and `.js`. 2. Find the default extension files and implement your custom code: - `-config.js`: The code in this file generally defines a method, `getConfig()`, that returns an object containing values used to configure your extension. Wix calls this method when you publish a site, and changes to the configuration don't take effect until you publish the site. - `.js`: The code in this file generally defines a method named after the purpose of the service plugin, such as `getShippingRates()` or `getFees()`. Wix calls this method to retrieve the data provided by the service plugin. For guidelines for writing your code, see the tutorial for each [available service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md). > **Note:** We recommend you use the standard export syntax contained in the code files when they're created. If you wish to use a different export syntax, for information about supported formats, see [Module Export Syntax](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md). ## Step 3 | Test the service plugin Before deploying your service plugin, test it thoroughly to ensure it works correctly and handles various scenarios. Testing prevents production issues and validates your implementation. At the end of this step, you'll have verified that your service plugin functions as expected and handles edge cases properly. To test your service plugin: 1. **Start with functional testing**: Test your service plugin methods directly in the code editor using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md). This lets you verify your logic without publishing your site. 2. **Add logging for debugging**: Include `console.log()` statements in your service plugin code to track execution flow and identify issues. After publishing, view these logs in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). 3. **Validate method responses**: Verify that your service plugin methods return properly formatted responses matching the expected schema for your specific service plugin type. 4. **Test edge cases and error handling**: Ensure your code properly handles: - Invalid or missing input parameters - Network timeouts or failures (for external API calls) - Empty or null data scenarios - Large data sets or high-volume requests 5. **Verify configuration (if applicable)**: If your service plugin uses a config file, test that the `getConfig()` method returns valid configuration data. 6. **Test on a test site before production**: After publishing your site once, test service plugin changes on a [test site](https://support.wix.com/en/article/about-test-sites) before deploying to production. Note: You must publish your production site at least once after implementing the service plugin for it to work on test sites. ## Step 4 | Deploy the extension This step publishes your service plugin to make it active on a site. At the end of this step, your service plugin is live and functional. After your code files are ready, publish the site to deploy the service plugin extension. ## Step 5 | (Optional) Remove an extension This optional step removes a service plugin from a site if you no longer need it. At the end of this step, the service plugin is completely removed from a site. To remove an extension: 1. In the **Public & Backend** section of the **Code** sidebar, under **Service Plugins**, hover over the extension's folder and click the **More Actions** icon. 2. Click **Delete**. ## See also - [About Service Plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md) - [Available Service Plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/available-service-plugins.md) - [Understanding the difference between service plugins and APIs](https://medium.com/wix-engineering/let-others-solve-your-problems-change-your-mindset-from-apis-to-spis-781afa48574f) - [Service plugins reference documentation](https://www.wix.com/velo/reference/spis/getting-started) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Available Service Plugins ## Article: Available Service Plugins ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/available-service-plugins.md ## Article Content: # Available service plugins The following service plugins are available: | Service Plugin | Description | Resources | | -------------- | ----------- | --------- | | Bookings Custom Pricing | Customize pricing for bookings to offer varied pricing to the bookings purchase flow. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/tutorial-bookings-pricing-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-bookings/bookings-custom-pricing) | | External Database Collection | Use external databases. | [Reference](https://dev.wix.com/docs/velo/events-service-plugins/data/service-plugins/external-database-collection-legacy/introduction.md) | | Billing Tax Calculation | Customize your tax calculations. | [Reference](https://dev.wix.com/docs/velo/events-service-plugins/billing/service-plugins/billing-tax-calculation/introduction.md) | | Automations Action | Write a custom action instead of selecting a pre-made action. | [Reference](https://dev.wix.com/docs/velo/events-service-plugins/automations/service-plugins/automations-actions/introduction.md) | | eCommerce Additional Fees | Add additional fees for things like location and special handling to your store's checkout flow. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-additional-fees-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-additional-fees) | | eCommerce Catalog (Beta) | Define your own custom catalog and how it interacts with the eCommerce purchase flow. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-catalog-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-catalog) | | eCommerce Discount Triggers | Apply discount rules to a site visitor's cart and checkout. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-custom-discount-trigger-service-plugin.md), [Reference](https://dev.wix.com/docs/velo/events-service-plugins/e-commerce/service-plugins/ecom-custom-discount-triggers/introduction.md) | | eCommerce Payment Settings | Apply custom payment settings during the payment process of an order. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-payment-settings-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-payment-settings) | | eCommerce Shipping Rates | Provide custom shipping rates fees to your store's checkout flow. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-shipping-rates-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-shipping-rates) | | eCommerce Validations | Validate a site visitor's cart and checkout. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-validations-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations) | | Payments Payment Provider | Provides functionality for integrating a site with payment providers not currently supported by Wix. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-payment-provider-service-plugin.md), [Reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider) | | Form Submission | Provides additional form submission validation. | [Tutorial](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-forms/tutorial-form-submission-service-plugin.md), [Reference](https://dev.wix.com/docs/velo/events-service-plugins/forms/service-plugins/form-submission/introduction.md) | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Site APIs ## Article: Exposing a Site API with HTTP Functions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md ## Article Content: # About Custom Site APIs Custom site APIs allow you to expose your site's functionality externally. This means that you, or other people, can access your site's functionality by calling your custom site APIs. Write and export [HTTP functions](#http-functions) to define custom site APIs. When you and others call the custom site APIs you defined, the corresponding HTTP function runs and the API responds with the HTTP function's return value.
Important: Custom site APIs are only intended for use in server-to-server communications. For example, don't use custom site APIs to set a cookie on a site visitor's browser as you may no longer be in compliance with applicable data privacy regulations.
For example, you might use custom site APIs to: - Integrate your site with an automation tool, such as [Zapier](https://zapier.com/) or [IFTTT](https://ifttt.com/). - Receive notifications or information from external webhooks or services. - Share a backend between your site and another application. ## Supported IDEs You can write http functions using: + The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). + The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). + Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor). ## HTTP functions The main components of an HTTP function are: - [HTTP method](#http-method) - [Function name](#function-name) - [Request parameter](#request-parameter) - [Functionality](#functionality) - [Response object](#response-object) ```js export function _ (request) { // functionality return response; } ``` ### HTTP method HTTP functions support the following HTTP methods: - `GET` - `POST` - `PUT` - `DELETE` To learn more, see [Methods for HTTP Functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md). ### Function name The function name is the unique identifier for your HTTP function in your site. It is also used to identify the function in the custom site API call. ### Request parameter The [request parameter](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/wix-http-function-request/introduction.md) is an object that is passed to the HTTP function. Use the request parameter for defining the details needed to process the HTTP function when it is called. The details generally include the request body, headers, URL, and other information. For all supported properties, see [`WixHttpFunctionRequest`](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/wix-http-function-request/introduction.md). ### Functionality Add code inside your HTTP function to run when your custom site API is called. Inside your HTTP function, you have full access to the full range of [Velo](https://dev.wix.com/docs/velo.md) and [SDK](https://dev.wix.com/docs/sdk/articles/get-started/about-the-wix-java-script-sdk.md) APIs and you can import and call functions defined elsewhere on your site. ### Response object Your HTTP function should return a [response object](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/wix-http-function-response/introduction.md). This is the response provided by the custom site API when it's called. There are several functions you can use to create a response object. Find them in the Velo [`wix-http-functions` module](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md). ## Authentication context Authentication context is information about who is calling an API. Calling Wix APIs with authentication context allows you to use functions that return different responses based on who calls them, such as [`getCurrentMember()`](https://dev.wix.com/docs/sdk/backend-modules/members/members/get-current-member.md). Depending on whether you need to include [authentication context](#authentication-context) in your request, there are different ways to call your custom site API: - [With authentication context](#with-authentication-context) - [Without authentication context](#without-authentication-context) ### With authentication context Call your API using the HTTP functions [REST API](https://dev.wix.com/docs/rest/assets/http-functions/rest/introduction.md) or [SDK module](https://dev.wix.com/docs/sdk/backend-modules/http-functions/functions/introduction.md). Provide these APIs with your HTTP function's details through the API's parameters and authorization. You must publish your site for your custom site APIs to be available through these APIs. ### Without authentication context Access your custom site APIs with different endpoints depending on the context in which you want to use them: - Test sites - Sites being built with [git integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) - Latest code in the editor - Production Learn more about [custom site API Calls](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md). ## HTTP Functions APIs There are different Wix APIs for exposing and calling custom site APIs: - [Velo HTTP Functions API](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md): Use this API to write and expose your custom site APIs. - [SDK](https://dev.wix.com/docs/sdk/backend-modules/http-functions/functions/introduction.md) and [REST](https://dev.wix.com/docs/rest/assets/http-functions/rest/introduction.md) HTTP Functions API: Use these APIs to call your custom site APIs with [authentication context](#authentication-context). ## See also - [Write an HTTP function](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/write-an-http-function.md) - [Methods for HTTP functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md) - [Custom site API calls](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Write an HTTP Function ## Article: Write an HTTP Function ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/write-an-http-function.md ## Article Content: # Write an HTTP Function HTTP functions define [custom site APIs](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md), which allow you to expose your site's functionality externally. This means that you, or other people, can access your site's functionality by calling your custom site APIs. HTTP functions determine the custom site API's HTTP method, endpoint, parameters, functionality, and response. [Calling a custom site API](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md) triggers your HTTP function to run. ## Step 1 | Create the file to host your HTTP functions Create a file called `http-functions.js` in your site's backend folder: 1. Start coding: - **Wix Studio**: Click on the **Code** icon in the left sidebar to open the code panel. - **Wix Editor**: Turn on Dev Mode. 1. Navigate to **Backend & Public** > **Backend**, and then create a file called `http-functions.js`.
Tip: You can also create the file with the following shortcut: 1. Click on the + next to **Backend**. 1. Click **Expose site API**.
## Step 2 | Create and export your HTTP function Your function's name must include the following parts joined by an underscore: - The [method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md) that should be used when calling your custom site API. - The [`functionName`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md#variables) that should be used in your custom site API's URL. ```js export function _ () {} ``` ## Step 3 | Code your HTTP function In your HTTP function, add the code you want to run when your custom site API is called. You can access the full range of functionality that you normally can when coding on a Wix site, including Wix's [Velo](https://dev.wix.com/docs/velo.md) and [SDK](https://dev.wix.com/docs/sdk/.md) APIs. As with all API calls, you can interact with a custom site API call in the following ways: - [Request](#request) - [Response](#response) ### Request Use the `request` parameter to provide your HTTP function with information from your custom site API call, including the `body`, `headers`, and `url`. For all supported properties, see [`WixHttpFunctionRequest`](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/wix-http-function-request/introduction.md). ```js export function _ (request) {} ``` You can then access the `request` object in the function in the same way as you'd access a parameter in any JavaScript function. ### Response Your HTTP Function should return a [response object](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/wix-http-function-response/introduction.md). This is the response sent when the custom site API is called. There are several functions you can use to create a response object. Find them in the Velo [`wix-http-functions` module](https://dev.wix.com/docs/velo/velo-only-apis/wix-http-functions/introduction.md). The response object supports the following properties: - `body`: Defined by you in the response function. - `headers`: Defined by you in the response function. - `status`: Defined by the response function. ## Step 4 | Keep your site secure (optional) HTTP functions expose your site's data and functionality to anyone who calls your custom site API, so it's recommended to authenticate who is calling your custom site API. One way to do so is by retrieving a secret key from the authentication header in the API request. This uses the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) and [`wix-secrets-backend`](https://dev.wix.com/docs/velo/apis/wix-secrets-backend/introduction.md). For example: ```js import { badRequest } from "wix-http-functions"; import { getSecret } from "wix-secrets-backend"; // This function compares the authorization key provided in the // request headers with the secret key stored in the Secrets Manager. async function isPermitted(headers) { try { const authHeader = headers.auth; const sharedAuthKey = await getSecret("secretEmail"); if (authHeader === sharedAuthKey) { return true; } return false; } catch (err) { console.error(err); return false; } } export async function get_functionName(request) { const headers = request.headers; if (!(await isPermitted(headers))) { const options = { body: { error: "Not authorized", }, headers: { "Content-Type": "application/json", }, }; return badRequest(options); } // Now add your code for authorized requests. } ``` > **Note:** If another Wix site is sending requests to your endpoints, you can use the [HMAC Authentication](https://dev.wix.com/docs/velo/articles/velo-package-readmes/wix-http-functions-hmac-authentication.md) Velo package for even more security. ## Step 5 | Debug your code Debug HTTP functions by adding `console.log()` calls to your function's code. The information you log appears in the function output when using [Functional Testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) and in your site's [Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md#debugging-with-wix-logs). The information logged by code that runs on the backend can also be viewed as [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). Wix Logs are accessible via [**Developer Tools > Logging Tools**](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/site-events?referralInfo=sidebar) on your site's dashboard. ## See also - [About custom site APIs](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md) - [Methods for HTTP functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md) - [Custom site API calls](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Methods for HTTP Functions ## Article: Methods for HTTP Functions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md ## Article Content: # Methods for HTTP Functions Each HTTP function contains a method definition that is used to call the corresponding [custom site API](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/expose-a-site-api-with-http-functions.md). The method is defined in the function declaration: ```js export function _(request) { } ``` HTTP functions support the following method declarations: - [`get`](#get) - [`post`](#post) - [`put`](#put) - [`delete`](#delete) - [`use`](#use) ## Methods ### `get` Calls your custom site API using the HTTP `GET` method. Usually, `GET` methods are used only to retrieve a resource. If the resource is found, your function should respond with a [200 (OK) status code](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/ok.md) and the requested resource. Learn more about [`get` HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/get.md). #### Example: Create a GET HTTP function that queries a collection to find items based on the path of the request ```js import { ok, notFound, serverError } from "wix-http-functions"; import wixData from "wix-data"; export function get_myFunction(request) { let options = { headers: { "Content-Type": "application/json", }, }; // Query a collection to find matching items. return ( wixData .query("myUserCollection") .eq("firstName", request.path[0]) .eq("lastName", request.path[1]) .find() .then((results) => { // Matching items are found. if (results.items.length > 0) { options.body = { items: results.items, }; return ok(options); } // No matching items are found. options.body = { error: `'${request.path[0]} ${request.path[1]}' was not found`, }; return notFound(options); }) // Something went wrong. .catch((error) => { options.body = { error: error, }; return serverError(options); }) ); } ``` ### `post` Calls your custom site API using the HTTP `POST` method. Usually, `POST` methods are used to create a new resource. If the resource is successfully created, your function should respond with a [201 (Created) status code](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/created.md) and a reference to the created resource. Learn more about [`post` HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/post.md). #### Example: Create a POST HTTP function that inserts an item from the request's body into a collection ```js import { created, serverError } from "wix-http-functions"; import wixData from "wix-data"; export function post_myFunction(request) { let options = { headers: { "Content-Type": "application/json", }, }; // Get the new item from the request body. return ( request.body .text() .then((body) => { // Insert the item in a collection. return wixData.insert("myUserCollection", JSON.parse(body)); }) .then((results) => { options.body = { inserted: results, }; return created(options); }) // Something went wrong. .catch((error) => { options.body = { error: error, }; return serverError(options); }) ); } ``` ### `put` Calls your custom site API using the HTTP `PUT` method. Usually, `PUT` methods are used to update a resource. If the resource is successfully updated, your function should respond with a [200 (OK) status code](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/ok.md). If the resource didn't exist so it was created, your function should respond with a [201 (Created) status code](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/created.md). Learn more about [`put` HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/put.md). #### Example: Create a PUT HTTP function that updates an item from the request's body in a collection ```js import { ok, serverError } from "wix-http-functions"; import wixData from "wix-data"; export function put_myFunction(request) { let options = { headers: { "Content-Type": "application/json", }, }; // Get the item from the request body. return ( request.body .text() .then((body) => { // Update the item in a collection. return wixData.update("myUserCollection", JSON.parse(body)); }) .then((results) => { options.body = { inserted: results, }; return ok(options); }) // Something went wrong. .catch((error) => { options.body = { error: error, }; return serverError(options); }) ); } ``` ### `delete` Calls your custom site API using the HTTP `DELETE` method. Usually, `DELETE` methods are used to delete a resource. If the resource is successfully deleted, your function should respond with a [200 (OK) status code](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/ok.md). Learn more about [`delete` HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/delete.md). #### Example: Create a DELETE HTTP function that deletes an item from a collection based on the path of the request ```js import { ok, serverError } from "wix-http-functions"; import wixData from "wix-data"; export function delete_myFunction(request) { let options = { headers: { "Content-Type": "application/json", }, }; // delete the item from a collection return ( wixData .remove("myUserCollection", request.path[1]) .then((results) => { options.body = { deleted: results, }; return ok(options); }) // something went wrong .catch((error) => { options.body = { error: error, }; return serverError(options); }) ); } ``` ### `use` Calls your custom site API using any HTTP method, unless there is another function with the same name defined for that specific HTTP method. For example, if you create 2 HTTP functions, called `get_myFunction` and `use_myFunction`, `GET` calls to `myFunction` will be handled by `get_myFunction`, but `POST`, `PUT`, and `DELETE` calls to `myFunction` will be handled by `use_myFunction`. Learn more about [`use` HTTP functions](https://dev.wix.com/docs/velo/api-reference/wix-http-functions/use.md). ## See also - [About custom site API](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/expose-a-site-api-with-http-functions.md) - [Write an HTTP function](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/write-an-http-function.md) - [Custom site API calls](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Site API Calls ## Article: Site API Calls ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/site-api-calls.md ## Article Content: # Custom Site API Calls Use the [endpoints](#endpoints) below to call your HTTP functions through custom site APIs. Call these endpoints with the [method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md) defined in your HTTP function. Make sure to define your [variables](#variables) correctly for the function.
Important: The endpoints in this article call custom site APIs without [authentication context](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md). You can [call your custom site APIs with authentication context](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md) using the HTTP Functions [REST API](https://dev.wix.com/docs/rest/assets/http-functions/rest/introduction.md) or [SDK module](https://dev.wix.com/docs/sdk/backend-modules/http-functions/functions/introduction.md).
## Variables You need to define the following variables to construct your API calls. ### `baseUrl` The base URL of the site hosting the HTTP functions: - **Premium sites**: `https://www.` - **Free sites**: `https://.wixsite.com/` > **Note:** The top level domain for free sites may not be `.com`. For example, for UK based sites, it may be `.co.uk`. ### `functionName` The name of the HTTP function your custom site API calls without its method prefix. For example, for an HTTP function defined by `export function get_myFunction(request) { }`, the `functionName` is `myFunction`. ## Endpoints Use the following endpoints to call your custom site APIs. ### Production Accesses the latest code published to the production site: `/_functions/` ### Test sites Accesses the latest code published to a [test site](https://support.wix.com/en/article/about-test-sites): `/_functions/?rc=test-site` ### Sites using [Git Integration](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) Accesses the code pushed in the specified branch (`branchId`) in the specified commit hash (`revisionNumber`): `/_functions/?siteRevision=&branchId=` ### Editor preview Accesses the latest code in the site editor: `/_functions-dev/` > **Note:** You must [create a test site](https://support.wix.com/en/article/test-sites-creating-a-test-site) to use this endpoint. ## See also - [About custom site APIs](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md) - [Write an HTTP function](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/write-an-http-function.md) - [Methods for HTTP functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/methods-for-http-functions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Packages ## Article: About Packages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md ## Article Content: # Velo: About Packages When writing code, you often need to build some complex feature or functionality. Sometimes you discover that someone else already wrote and implemented the functionality that you need. Instead of reinventing the wheel and writing the same code over again, you can find a pre-built package or app written by others and incorporate it into your own code. Using pre-built packages, also known as code libraries, is a great way to minimize your development time and the amount of code you need to write. There are 2 types of pre-built packages available to add to your Velo site: * [Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) * [npm Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) You can add packages to your site in the Package Manager, which you can access from the **Packages & Apps** section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). To add a package, click **Install Velo Package** or **Install packages from npm**. Then select the package you want and click **Install**. For more information, see the articles for each package type. If there isn't already a package that meets your needs, you can also create your own [custom app](#custom-apps) to reuse code and other functionality across multiple sites. ### Velo Packages Built using the Velo APIs, Velo packages are created specifically for Wix websites and work seamlessly with Wix elements and apps. Unlike npm packages, Velo package use cases are very specific to your Wix site. A Velo package can include backend code files, public code files, and npm packages. A Velo package's functionality is available for use through its exported functions. An editable **config.json** file may also be included for defining specific settings for a package. You can import the functions and use them in your site code just like you import and use exported functions that you write in .js and .web.js files. > **Note:** If a Velo package includes an npm package, you won't see the npm package in the npm section of the Code sidebar (Wix Editor) or Code sidebar (Wix Studio). Velo packages built by Wix allow you to add popular complex functionality to your site without spending hours writing code. Choose the Velo package you want, and import it on each site where you want to add the specific functionality. You can view all the code used to implement a package. Each Velo package contains a detailed README file explaining the package's functionality and the steps required for implementation. Some package functions are internal and can't be used directly in your site. See the package's README for the list of exposed functions available for use. To help you understand how to use specific Velo packages on your site, we added some examples to the [Coding Examples page](https://dev.wix.com/docs/coding-examples.md) that integrate Velo packages. Look for the **Velo package** tag ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b4ae9703-4364-4550-a797-1cc95ed8046e/2021/11/24/fcdc7965-604e-4777-b2e0-782fe556303e/4f1a6077-5246-4368-bb9a-223d042d533a.png)  in the example. To learn more, see [Working with Velo Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md).   ### npm Packages npm is the most popular registry of pre-built packages. The packages are built and maintained by npm developers. Velo allows you to install public npm packages in your site directly from the npm registry. It is your responsibility to follow the npm package's developer guidelines to understand the package's functionality, in what situations it can be used, and in what situations it should not be used. Once installed, you can import the package and use it in your code. To learn more, see [Working with npm Packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md). ### Custom Apps You can build custom apps using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) to share custom functionality across your sites. Once a custom app is installed on your site, the site can import custom app code, use the custom app's widgets, and the custom app's [dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) and [collections](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md) are automatically added to the site. Learn more about [custom apps](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-custom-apps.md) and how to [install custom apps on a site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About npm Packages ## Article: About npm Packages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md ## Article Content: # About npm Packages [Node Package Manager (npm)](https://www.npmjs.com/) is a popular registry of reusable JavaScript code. In npm, each reusable library of code is referred to as a package. When developing Wix sites, you can utilize the following types of packages from npm: - **Wix packages**: Packages from the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md). Use these packages to create an interactive UI, work with data-driven content, and integrate with Wix business solutions and 3rd-party services. - **Non-Wix packages**: Packages written by 3rd-party developers. Use these packages to save time instead of redeveloping functionality that already exists. Once an npm package is installed, you can import it in your site's code and use it. Here are some examples of npm packages you could use: - `@wix/data`: Manage data in your Wix CMS. - `@wix/stores`: Manage product and inventory for Wix Stores. - `@wix/site-stores`: Manage the current cart and retrieve information about product variants and availability. - `lodash`: Manipulate strings, arrays, and optimize common operations. - `@sendgrid/mail`: Easily send emails using the SendGrid service. - `axios`: Make HTTP requests from a browser or Node.js environment. ## Supported IDEs You can use npm packages with: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor) >**Note:** You can't install or update npm packages directly from the Wix IDE, but you can use packages that were installed or updated using the editor. Follow [instructions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md) for installing and updating packages in the editor, then import and use the package in the Wix IDE. ## Package examples Check out the following example sites and templates that integrate npm packages: - QR Code Generator ([example site](https://www.wix.com/velo-examples/qr-generator) | [template](https://manage.wix.com/edit-template/from?originTemplateId=f603d9ce-4a52-467d-8907-22f183b3048f&templateId=a288d304-aecb-477d-821a-1cad43966ab1&autoDevMode=true)) - Send SMS on Booking Confirmation ([example site](https://www.wix.com/velo-examples/send-sms-on-booking) | [template](https://manage.wix.com/edit-template/from?originTemplateId=5012203a-d11f-47bf-a8e9-bc41670e7d29&templateId=72a3c87b-938c-4a84-a080-6a7a83930f2f&autoDevMode=true)) - Sendgrid NPM integration ([example site](https://www.wix.com/velo-examples/sendgrid-integration) | [template](https://manage.wix.com/edit-template/from?originTemplateId=8dd1771f-921f-4aee-8a99-9f0f3209dacc&templateId=$c515c94c-3568-45c2-bbcc-f46fe9124d77&autoDevMode=true)) - Mega Search ([example site](https://www.wix.com/velo-examples/mega-search)| [template](https://manage.wix.com/edit-template/from?originTemplateId=0b0cd7d9-4d16-428c-b143-dfbe6f7b3e6c&templateId=048131c8-3cb8-48b6-b1b8-3b453d5182f7&autoDevMode=true)) ## Package support When you install a non-Wix npm package on your site, it's important that you understand the package's functionality, appropriate use cases, and limitations. Note that certain types of packages are not supported, including [private packages](https://docs.npmjs.com/about-private-packages), packages requiring specific hardware, and packages that may pose a security risk. Here are some important considerations for using non-Wix npm packages in your code: - **Package security:** Packages are 3rd-party code and are not verified by Wix. Therefore, installing them can put your site and users at risk. It is your responsibility to make sure that packages are safe before installing them on your site. - **Frontend vs backend code:** Make sure you use packages in the context they were designed to be used. Some packages are intended exclusively for frontend code, while others are meant for backend code. - **DOM interactions:** With Wix, you use [\$w APIs](https://dev.wix.com/docs/velo/velo-only-apis/$w/$w.md) to interact with page elements instead of directly manipulating the DOM. Therefore, some packages that include interaction with the DOM may have limited use in your site. - **React compatibility:** Some packages are designed to work with [React](https://reactjs.org/). Currently, you can only use these packages in conjunction with [custom elements](https://dev.wix.com/docs/velo/velo-only-apis/$w/custom-element/introduction.md). - **Error handling:** Errors that originate from a package's functionality are reflected in the browser console. These errors are generated by the implementation of the package itself, and not from Wix. Refer to the package's documentation for more information on resolving these errors. ## Performance considerations - Importing npm packages in frontend code increases the page's payload and may adversely affect your site's performance. ## Package limitations The non-Wix packages you install and use on your site must be compatible with Wix's runtime environment or they may not run properly. - ES modules and packages containing native modules are incompatible with Wix. - A package won't execute during runtime unless it aligns with Wix's [current Node.js version](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/java-script-support.md). - Wix has runtime limitations: terminal commands are not supported. - For a package to be compatible with Wix, all its dependent packages must also be compatible. Otherwise, the package may not run. ## See also - [About packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) - [Work with npm packages in the editor](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md) - [Work with npm packages with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md) - [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Work with npm Packages in the Editor ## Article: Work with npm Packages in the Editor ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md ## Article Content: # Work with npm Packages in the Editor Wix allows you to install [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) on your site. This article explains how to install, update, and uninstall npm packages when working in the editor (Wix Studio and Wix Editor) or Wix IDE. You can also use npm packages in your local IDE with the [Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md). >**Wix IDE users:** You can't install or update npm packages directly from the Wix IDE, but you can use packages that were installed or updated using the editor. Follow the instructions below for installing and updating packages in the editor, then import and use the package in the Wix IDE.
## Install an npm package Before using an npm package, you need to install it on your site.
__Important:__ By installing an npm package, you agree to that npm module's license agreement.
To install an npm package in the editor: 1. Navigate to **Packages & Apps** in the code panel. 1. Hover over **npm** in Packages & Apps, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/03/07/30545cd2-342e-4223-9445-e7760e65566d/b2605497-443f-4089-a367-71c037b85423.png), and select **Install npm package**. The Package Manager opens. Note that the 10 most popular npm packages used on Wix sites are displayed in the Package Manager.
1. In the npm section of the Package Manager, search for the package you want to add to your site. Instructions for how to use the package are displayed on the right side of the Package Manager. 1. Click **Install** next to the package of your choice. Once installed, you can find the package under npm in the Packages & Apps section of the Code sidebar. 1. Learn how to use the installed package from its README file. There are several ways to view the README file: + In the **Packages & Apps** section of the Code sidebar, click the npm package to view the README file in the code editor. + Hover over the package, click the **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/347cc5c6-bce3-4407-82c3-e211e02cab98/398ced84-9ef5-4f70-87cd-4cb39a66dd65.png) icon, and then select **View Documentation** to view the npm package documentation in a new browser. + Search for your package's README file at [npmjs.com](http://npmjs.com/). ## Change a package's version In the editor, you can update an npm package to its latest version, or you can change it to a previous version.
**Important:** After changing a package's version, make sure that the code on your site is compatible with the updated version of the package.
To change a package version in the editor: 1. Navigate to **Packages & Apps** in the code panel. 1. Hover over **npm** in Packages & Apps, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/03/07/30545cd2-342e-4223-9445-e7760e65566d/b2605497-443f-4089-a367-71c037b85423.png), and select **Install npm package**. 1. In the Package Manager, select **Installed Packages**. 1. Hover over the package you want to update, click the **More Actions** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/347cc5c6-bce3-4407-82c3-e211e02cab98/398ced84-9ef5-4f70-87cd-4cb39a66dd65.png) icon, and select **Choose a version**. ![Change version](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3efd4b23b92dda69d0bf42d8ae95b491.png) 2. Find the version you want and click **Install**. ![npm versions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6122f99e11216c28784295483494b29e.png) ## Uninstall a package You can uninstall npm packages that you no longer need.
**Warning:** When you uninstall a package, make sure to update any code that relies on that package.
To uninstall a package in the editor: 1. Navigate to **Packages & Apps** in the code panel. 2. In the npm section, hover over the package you want to uninstall, click the **More Actions** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/347cc5c6-bce3-4407-82c3-e211e02cab98/398ced84-9ef5-4f70-87cd-4cb39a66dd65.png) icon, and select **Uninstall**. ## See also - [About packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) - [About npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) - [Work with npm packages with the Wix CLI for Sites](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Work with npm Packages with the Wix CLI ## Article: Work with npm Packages with the Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md ## Article Content: # Work with npm Packages with the Wix CLI for Sites Wix allows you to install [npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) on your site. This article explains how to install, update, and uninstall npm packages in your local IDE with the Wix CLI for Sites. You can also [use npm packages in the editor or the Wix IDE](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md). ## Install an npm package Before using an npm package, you need to install it on your site.
__Important:__ By installing an npm package, you agree to that npm module's license agreement.
To install a package in your local IDE with the Wix CLI for Sites: 1. Open a terminal to your project's root directory. 2. Install the package of your choice by running the following command: ```bash wix install ``` 3. Open the `package.json` file in your project to see the installed package. 4. Learn how to use the installed package from its README file. You can find your package's README file at [npmjs.com](http://npmjs.com/). ## Change a package's version Using the Wix CLI for Sites, you can update an npm package to its latest version. Currently, you can't switch to previous versions of your package using the Wix CLI for Sites.
**Important:** After changing a package's version, make sure that the code on your site is compatible with the updated version of the package.
To update a package to its latest version with the Wix CLI for Sites: 1. Open a terminal to your project's root directory. 2. Update the package of your choice by running the following command: ```bash wix update ``` 3. Open the `package.json` file in your project to see the package's new version. ## Uninstall a package You can uninstall npm packages that you no longer need.
**Warning:** When you uninstall a package, make sure to update any code that relies on that package.
To uninstall a package using the Wix CLI for Sites: 1. Open a terminal to your project's root directory. 2. Uninstall the package of your choice by running the following command: ```bash wix uninstall ``` 3. Open the `package.json` file in your project to see that the package has been removed. ## See also - [About packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-packages.md) - [About npm packages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) - [Work with npm packages in the editor](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Velo Packages ## Article: Working with Velo Packages Built by Wix ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md ## Article Content: # Velo: Working with Velo Packages Velo packages are code libraries written using Velo code that allow you to add specific functionality to your site, without spending hours writing complex code. You can add Velo packages to your site. If no existing Velo package meets your needs, you can [create a custom app with Wix Blocks](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-custom-apps.md). These are some of the advantages of using Velo packages: * The use cases for Velo packages are specific to Wix websites. * Velo packages work seamlessly with Wix elements and apps.  * You can view all the code used to implement a Velo package. * Each Velo package contains a detailed README file explaining the package's functionality and the steps required for implementation. ### Package Content Velo packages can include read-only backend code files, public code files, and npm packages. An editable **config.json** file may also be included for defining specific settings for your package. A package's functionality is available for use through its exported functions, which can be imported and used in your code. >**Notes** > * Some package functions are internal and can't be used directly in your site. See the package's README for the list of exposed functions available for use. > * If a Velo package includes an npm package, you won't see the npm package in the npm section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). ### Setting Up a Package Velo packages can work with Wix apps or include integrations with 3rd-party services, and may require initial setup and configuration before use. To set up your site to use a package, follow the steps in the **Setup** section of the package's README file. Package prerequisites may include:  * Creating an account with a 3rd-party service. * Storing API keys and other secrets from a 3rd-party service in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md).  * Adding a Wix App to your site. For example, you might need to add a Wix Store or a Member's Area to your site. * Editing the package's **config.json** file to define specific settings that affect how the package works on your site. #### Backend Events A package may also include an **events.js** file for running [backend events](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/events/about-backend-events.md) related to the package. You do not need to perform any action to run the backend events. Just like backend events are defined in your site's **events.js** file, events in your package's **events.js** are triggered automatically once you have published your site and the specific conditions associated with the event are met. ### Installing a Package To install a package: 1. Go to the **Packages & Apps** section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). Under **Velo Packages** click **Install Velo Package**. 2. The Package Manager opens. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d2d71c02e760dbf7e5e75630ca768d7b.png)
3. In the **Built by Wix** tab, search for the package you want to add to your site. Note that you can view the README for each package on the right side of the Package Manager. 4. Click **Install**. The **Install** button changes to a **View** button, and you can see an option to turn on automatic updates for future minor versions. When this option is turned on, you won't need to do anything if there are any minor updates to the package. When this option is turned off, you'll have to manually update the package if there are any minor updates to the package. Clicking **View** directs you to the **Packages & Apps** section of the Code sidebar (Wix Editor) or Code sidebar (Wix Studio) where you can see the installed Velo package. If there is a major update available for a package installed on your site, or if there is a minor update available and the automatic updates option toggle is turned off, you'll see a blue dot next to the package.  >**Note** > By installing a Velo Package that uses an npm package, you agree to that npm module's license agreement. ### Using an Installed Package Once you install a package, read the package's README file. The README includes all the information you need to work with the package: initial setup prerequisites, which exported functions are available for use, and how to use the package.  To use the package functions, you'll need to import them in your code.  Use the **@velo namespace** to import Velo packages. Note that import statements within the **@velo namespace** differ depending on the location and type of package file the function is exported from.  To import backend functions, use the following syntax: ```javascript import { } from '@velo/-backend'; ``` For example, assuming the **twitter-integration** package has been installed, you can use its **tweet()** function as follows: ```javascript import { tweet } from '@velo/twitter-integration-backend'; ``` To import public functions, use the following syntax: ```javascript import { } from '@velo/'; ``` For example, assuming the **wix-data-helpers** package has been installed, you can use its **getRandomItem()** function as follows: ```javascript import { getRandomItem } from '@velo/wix-data-helpers'; ```
**Notes** * If you want to use backend functions from a .js file in your page code, wrap the function in a [web module](https://support.wix.com/en/article/velo-web-modules-calling-server-side-code-from-the-front-end), and import the web module to your page code. * Some Velo packages include web modules. You can call web module functions directly from your backend code, public code, or page code. Import them with the syntax for importing backend functions.
To learn more about which import statements to use for each Velo package, see the package's README file. #### Package Examples To help you understand how to use specific Velo packages on your site, we added some examples to the [Coding Examples site](https://dev.wix.com/docs/coding-examples.md) that integrate Velo packages. You can open an example site and see the code we added to work with the package. You'll see a Velo Package tag on examples that integrate a Velo Package. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/03/09/d1e90191-541b-4137-b707-555c787c0cb3/3c5630de-e7cb-4c1b-9227-cad7618faacd.png)
**Note:** These package examples and their steps are based on Wix Editor sites. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#finding-your-way) features.
### Updating a Package's Version If there is a **minor update** to a package installed on your site: * If the automatic updates option toggle is turned on, the package will update automatically.  * If the automatic updates option toggle is turned off, you'll see a blue dot next to the package in the Code sidebar.  If there is a **major update** to a package installed on your site, you'll see a blue dot next to the package in the Code sidebar.  To update the package on your site: 1. Hover over the dot and click **See Release Notes** in the tooltip. The Package Manager opens and you can see the available update. 2. In the README on the right side of the Package Manager, scroll down to the Release Notes section to view the relevant changes for this update. 3. If you want to update the package, click **Update to X.X.X**. You can view all versions of a package by clicking the ellipsis icon **>** **See all versions**. 
**Important** After applying a package update, make sure that the code on your site is compatible with the updated version of the package.
### Uninstalling a Package To uninstall a package: 1. In the **Velo Packages** section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio), hover over the package you want to uninstall. Click the ellipsis ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/347cc5c6-bce3-4407-82c3-e211e02cab98/398ced84-9ef5-4f70-87cd-4cb39a66dd65.png) icon, and select **Uninstall**. 2. The package is removed from the **Velo Packages** section of the Code sidebar (Wix Editor) or the Code Sidebar (Wix Studio).
**Warning** When you uninstall a package, you may break code that relies on that package.
### Requesting a Package To request that a package be added to the list of Velo packages built by Wix, click **Make a request for a new Velo package** at the bottom of the Package Manager. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Apps ## Article: Working with Your Velo Packages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-custom-apps.md ## Article Content: # About Custom Apps > **Note:** Custom apps replace Velo packages built by you. Now, instead of creating packages, functionality is managed as apps. Custom apps are applications you create using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) to add functionality that can be used across multiple Wix sites. Your custom apps are private and only you can install them on sites where you're a site contributor. Once installed, all site contributors can access the functionality of the custom app. Using custom apps allows you to unify the following functionality across your sites: - Widgets - Code - [Dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) - [Collections](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md) For example, you might use custom apps to do the following across your sites: - Ensure a standardized visual design. - Centralize and reuse custom logic and code. - Add and manage custom dashboard features. - Use uniform data structures and content setups. ## Supported IDEs You can use and access functionality from your custom apps using: - The editor (Wix Studio and Wix Editor) - The Wix IDE - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) > **Note:** You must have a Wix Studio account to create a Blocks app. ## Accessing custom app functionality on a site Install and manage your custom apps, including the version installed on the site, under **Custom Apps** when you click **App Market** ![App Market](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b748dd69307d0adbcf2f11e76dc779c3.png) in the Editor. Once installed, a custom app's functionality is automatically available on your site. To learn how to access the functionality, see [Install a Blocks App on a Site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md#change-the-installed-app-version). ## See also - [Install a Blocks app on a site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md#change-the-installed-app-version) - [About Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Routers ## Article: About Routers ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md ## Article Content: # About Routers Wix gives you the ability to display multiple items from your data collection with [dynamic pages](https://support.wix.com/en/article/cms-about-dynamic-pages). In your site editor, you see only one dynamic item page, but visitors to your site see multiple pages with the same design, each rendering a different item from your collection. You can set a unique URL for each of these item pages. Behind the scenes, Wix creates a router that handles incoming requests to the dynamic page and directs the client to the correct URL. Wix also handles SEO for you in the background. However, if you prefer to handle requests with your own custom logic, you can create your own router. With your own router, you have full control over how your site handles incoming requests, as well as the SEO for dynamic pages. You set up a router to receive all incoming requests with a specified prefix, and define the logic of what to do when a request with that prefix is received. You decide what actions to perform, what response to return, where to route the request, and what data to pass to the page. You might want to use a router to: * Display a dynamic page using content from any data source. * Customize your URLs to make them more meaningful and yield better SEO results. * Authenticate users and then display content just for them. * Return custom HTTP response codes. In the following sections, we review the important components of creating a router for your site. ## Supported IDEs You can create routers using: * The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). * The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md). * Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor). ## URL prefix When you create a router, you specify a URL path prefix that follows your site's domain name. The prefix is the part of the URL shown in angle brackets in the following examples: * Premium site: `https://domain.com//category/item` * Free site: `https://user.wixsite.com/yoursite//category/item` This URL prefix determines which incoming requests your router handles. If your site receives a request with the specified prefix, it sends that request to your router for handling. The URL prefix is also used as the name for the router functions and pages. ## Routing code When you create the first router on your site, Wix automatically adds a file called `routers.js` to your site's backend folder. In this file, you write all your logic for handling incoming requests. `routers.js` contains two functions that are the entry points to your router. They are named with the following convention: * `_Router(request)` * `_Sitemap(sitemapRequest)` Note that if you add any more routers to the site, Wix adds the functions for them in the same file. For example, if you add `myRouter1` and `myRouter2` to the site, you'll see the following functions in your `routers.js` file: * `myRouter1_Router(request)` * `myRouter1_Sitemap(sitemapRequest)` * `myRouter2_Router(request)` * `myRouter2_Sitemap(sitemapRequest)` You then write the logic for each router in its respective set of functions. ### router() The [`router()`](https://dev.wix.com/docs/velo/api-reference/wix-router/introduction.md) function is where the site sends page requests with the defined prefix. The router receives a `WixRouterRequest` object containing information about the incoming request, such as the full URL used to reach the router, and where the request came from. The function then decides what to do with the request and returns the appropriate `WixRouterResponse`. Typically, the `router()` function will decide which page to show (if any) and what data to pass to the page. The response is then sent using the `forbidden()`, `notFound()`, `ok()`, `redirect()`, or `sendStatus()` functions. ### sitemap() Your sitemap is what Google uses to find your site's pages. Pages on your site that don't belong to a router are automatically added to your sitemap for you. However, since you control what pages are available through your router, you need to create your own sitemap for these router pages. The router sitemap contains all possible URLs that are connected to your router's prefix, so that Google can find each router page. The [`sitemap()`](https://dev.wix.com/docs/velo/api-reference/wix-router/sitemap.md) function handles the SEO and sitemap requests for your router. In the code for your  `sitemap()` function, you need to create and return a [`WixRouterSitemapEntry`](https://dev.wix.com/docs/velo/api-reference/wix-router/wix-router-sitemap-entry.md) object for each router page. This ensures search engines can find the links to your router's pages. As with [SEO for regular pages](https://support.wix.com/en/article/understanding-your-pages-default-seo-settings), you can define the page title, description, and social network images for router pages. The difference is that since router pages don't contain static data, their SEO information must be set dynamically, so they reflect the real content they will hold when they are viewed. Each `WixRouterSitemapEntry` includes information about a page, such as its URL, title, and name. You can also add additional information about each page, such as how often its content changes, when the last change was, and its relative priority within your site. Google uses the sitemap entries to discover all the pages in your router. The `sitemap()` function is also used to populate the items preview widget when you're previewing or editing your site, allowing you to switch between dynamic item pages. ![Items preview widget on Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5e73a3dbd3103e4d592d834bdcaa5c92.png) ## Router data Your `router()` function may send data to the pages it routes to. You can access that data in the frontend page code using the [`getRouterData()`](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/get-router-data.md) function of the `wix-window-frontend` module. ## See also * [Create a router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) * [About SEO and routing](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md) * [Tutorial: Create dynamic pages with a custom router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/tutorial-create-dynamic-pages-with-a-custom-router.md) * [About Router Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-router-caching.md) * [wix-router API](https://dev.wix.com/docs/velo/api-reference/wix-router/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Router ## Article: Creating a Router ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md ## Article Content: # Create a Router Creating a [router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) allows you to customize how your site handles certain incoming requests. This article shows you how to create a router for your site. ## Step 1 | Add the router in the site editor Whether you're working in the editor, [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md), or your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md), in order to add a router to your site you must first set it up in the site editor. If you skip this step, Wix won't create the frontend router pages that are displayed to site visitors, and you won't have another way to add them. Once you create your router in the site editor, you can add the code in your preferred IDE. The following steps explain how to add a router to your site: **Wix Studio**: 1. Make sure you [enable coding](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) on your site. 1. Click the **Pages** icon ![](https://github-production-user-asset-6210df.s3.amazonaws.com/91874936/255611116-bbe4fa95-9281-4246-9780-3a3641cc8bf5.png) to open the **Site Pages** panel.
1. Click the **Add New Page** icon ![](https://github-production-user-asset-6210df.s3.amazonaws.com/91874936/255386869-eac62946-357a-44c4-800c-cf8c8735b77f.png). Then, click **Add** under Router. ![Add router](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8a241a042bf517eb91cd8c0739ebd206.png) >**Note:** You can also add a router from the **Page Code** section of the code panel. Hover over **Main Pages**, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/04083fa4-3262-4606-98d1-cbc359bf8585/1a26226e-c281-42d2-8a60-88e44fe40c61.png), and then click **Add a Router**. 1. Enter a [URL prefix](https://support.wix.com/en/article/velo-about-routers#url-prefix) for your router and click **Add & Edit Code** to add the router to your site. All incoming requests with the specified URL prefix will be sent to your router for handling.
**Wix Editor**: 1. In the sidebar, go to **Page Code** > **Main Pages**. Click on the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/06/11/40411987-4cca-456b-8449-c356cf794845.png) icon at the top of the **Main Pages** section, and choose **Add a Router**. ![Add a router in Wix Editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f66c7fc39166fa58aa6209085103339d.png) 2. Enter a [URL prefix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) for your router and click **Add & Edit Code**. All incoming requests with the specified URL prefix will be sent to your router for handling. After you add a router, the following occurs: **If you're working in the editor:** * Your router's [`router()`](http://wix.to/94BuAAs/wix-router.html#router) and [`sitemap()`](http://wix.to/94BuAAs/wix-router.html#sitemap) functions are added in a `routers.js` file with sample code for a simple routing scenario. The `routers.js` file is located in the **Backend & Public** section of the code panel. * A new section called **Router Pages** is created in the **Page Code** section of the code panel. Router pages are grouped together under a title based on the prefix you chose earlier. One router page is created to start with. For example, if you named your router "myRouter", a page named **myRouter-page** is added under the title **MyRouter Pages (Router)**. **If you're working in the Wix IDE:** * Your router's [`router()`](http://wix.to/94BuAAs/wix-router.html#router) and [`sitemap()`](http://wix.to/94BuAAs/wix-router.html#sitemap) functions are added in a `routers.js` file with sample code for a simple routing scenario. The `routers.js` file is located in `src/backend`. * In the site editor, a new section called **Router Pages** is created in the **Page Code** section of the code panel. Router pages are grouped together under a title based on the prefix you chose earlier. One router page is created to start with. For example, if you named your router "myRouter", a page named **myRouter-page** is added under the title **MyRouter Pages (Router)**. Add any router pages in the site editor. In the Wix IDE, those pages then appear under `src/pages`. Note that in the Wix IDE the router pages aren't grouped by router, so if you create more than one router, name each router page clearly to avoid confusion. ![Difference between router pages in Wix IDE and site editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5bb83ef4321be58f835c8bf3a31c696e.png) **If you're working in your local IDE:** * A modal opens in your site editor containing sample code and instructions on how to add the code to your site. We recommend copying the code to make sure your router and sitemap functions are set up correctly. ![Local IDE router instructions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/056d1c28d7cbdd25ab8e4cb5ba1bbbf3.png) * A new section, **Router Pages**, is created in **Page Code** section of the code panel. Router pages are grouped together under a title based on the prefix you chose earlier. One router page is created to start with. This page and any other router pages you create are added to your github repository. You can then pull them to your local repo and write the code in your IDE. > **Note:** Certain module export formats are not supported in `routers.js`. For more information, see [Module Export Syntax](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md). ## Step 2 | Coding the router Once you add the router to your site, you need to add code for it. The code for the router works the same way no matter which IDE you're working in. All your router logic goes in the `routers.js` file. There are four parts to the sample code that's added to the `routers.js` file: 1. **The `import` statement:** The default code imports some basic router functionality from the [Router API](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/introduction.md), including the [`ok()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/ok.md) and [`notFound()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/not-found.md) functions, as well as the [`WixRouterSitemapEntry`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/wix-router-sitemap-entry.md) object. ```javascript import {ok, notFound, WixRouterSitemapEntry} from "wix-router"; ``` Keep this `import` statement when you customize your code, and use it to add any further functionality from the Router API. You may also need to import additional modules, such as the [Wix Data API](https://dev.wix.com/docs/velo/apis/wix-data/introduction.md). 2. **The sample data:** The sample code includes an object called `peopleData`, which is the data the sample router displays on the router page. For your own code, delete or replace this object. You can use the Data API to [retrieve data items](https://dev.wix.com/docs/velo/apis/wix-data/query.md) from a collection, as explained in our [router tutorial](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/tutorial-create-dynamic-pages-with-a-custom-router.md). 3. **The [`router()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/router.md) function:** Every time you add a router to your site, Wix adds a `router()` function for it in `routers.js`. The first `router()` function added contains sample code like this: ```javascript export function myRouter_Router(request) { // Get item name from URL request const name = request.path[0]; // Get the item data by name const data = peopleData[name]; if (data) { //define SEO tags const seoData = { title: data.title, description: `This is a description of ${data.title} page`, noIndex: false, metaTags: [ {"og:title": data.title, "og:image": data.image, content: "People Data" } ] }; // Render item page return ok("myRouter-page", data, seoData); } // Return 404 if item is not found return notFound(); } ``` Let's quickly review what the sample code is doing: **Lines 3-6:** The router gets the path from the `request` parameter, which is a [`WixRouterRequest`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/wix-router-request/introduction.md) object, and searches for it in the `peopleData` object. **Lines 8-27:** If it finds the requested name, it creates a [`HeadOptions`](https://www.wix.com/velo/reference/wix-router/wixrouterresponse/head) object that defines what goes in the HTML head of the page we respond to the request with. That object is stored in the `seoData` variable. The router then passes the data from `peopleData` and `seoData` to the [`ok()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/ok.md) function, which renders the requested page to the visitor. **Line 31:** If the person isn't found in `peopleData`, the router returns a 404 page by using the [`notFound()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/not-found.md) function. Build off the code above to customize your `router()` function: 1. Replace lines 3-6 with your own code to retrieve data from an external source or a collection on your site. 1. Change the conditional statement to match your own router logic. For example, if you need to display either an index or an item page, build a logical statement that renders one or the other based on the contents of the `path` parameter. 1. Change the [`HeadOptions`](https://www.wix.com/velo/reference/wix-router/wixrouterresponse/head) object to generate your SEO data. You can use any information you want to create the object. For example, the sample code creates `title` and `description` properties based on data from `peopleData`. You can create your own properties from the data you retrieve and populate `metaTags` with this data as well. You also have the option, as in the sample code, to set `noIndex` to false, meaning search engines should index the page, or add a `keywords` property to the [`HeadOptions`](https://www.wix.com/velo/reference/wix-router/wixrouterresponse/head) object with a string containing the page's keywords. 1. Your router function must return a [`WixRouterResponse`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/wix-router-response/data.md) object. This object is responsible for telling the router which page to display on the frontend when a visitor makes a request: * Use the `ok()` function to render a requested page if it exists. Pass three arguments to `ok()`: * The name of the router page to render. * A data object containing the data to pass to the router page. * An object containing SEO data for the router page. * Use the `notFound()` function if the requested page isn't found. You can also use the [`forbidden()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/forbidden.md), [`redirect()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/redirect.md), or [`sendStatus()`](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/send-status.md) functions. 4. **The [`sitemap()`](http://wix.to/94BuAAs/wix-router.html#sitemap) function:** Along with the `router()` function, Wix also adds a `sitemap()` function to `routers.js` every time you add a router. As with `router()`, the first `sitemap()` function added includes sample code that you should replace with your own. Let's take a look at the sample code in the `sitemap()` function: ```javascript export function myRouter_SiteMap(sitemapRequest) { // Convert the data to site map entries const siteMapEntries = Object.keys(peopleData).map((name) => { const data = peopleData[name]; const entry = new WixRouterSitemapEntry(name); entry.pageName = "myRouter-page"; // The name of the page in the Wix Editor to render entry.url = `/myRouter/${name}`; // Relative URL of the page entry.title = data.title; // For better SEO - Help Google return entry; }); // Return the site map entries return siteMapEntries; } ``` **Lines 3-10:** The code takes the keys of the `peopleData` sample object and uses the JavaScript `map()` function to create an array of [`WixRouterSitemapEntry`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md) objects, one object for each key. Each entry is given values for the `pageName`, `url`, and `title` properties. Then the array is wrapped in a `Promise` and returned. Replace the sample code with code of your own that generates `WixRouterSitemapEntry` objects for each page. Then return these objects for SEO to use. Once you complete these steps, you have a working router. Publish your site and make page requests to test the router. ## See also * [About routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) * [About SEO and routing](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md) * [Tutorial: Create dynamic pages with a custom router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/tutorial-create-dynamic-pages-with-a-custom-router.md) * [About Router Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-router-caching.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add SEO to Your Router ## Article: About SEO and Routing ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md ## Article Content: # Add SEO to Your Router This article explains how to configure [SEO for your router pages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md#sitemap). There are two parts to setting up the SEO: 1. Create meta tags for Google to learn about your pages. 1. Create a sitemap for Google to use to find your pages. ## Step 1 | Set meta tags for router pages You set SEO meta tags for your router's pages in the `router()` function in [`routers.js`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md#routing-code): 1. Within the `router()` function, create an object that contains your desired SEO settings. This will become the [`HeadOptions`](https://www.wix.com/velo/reference/wix-router/wixrouterresponse/head) object that you pass to the router page. Use the provided sample code in the `router()` function as a starting point: ```javascript let seoData = {     title: data.title,     description: "This is a description of " + data.title + " page",     noIndex: false,     metaTags: {       "og:title": data.title,       "og:image": data.image,     },   }; ``` The example object contains several properties that are set using the data it retrieves in the `router()` function, including the following: - The `title` property, which contains the page’s SEO title. - The `description` property, which contains a short description of the page’s content. - A `noIndex` flag, set to `false`. This tells Google that it should index your page. - Additional meta tags contained in the `metaTags` property. You can add meta tags to this property. Keep or remove these properties, and add your own as needed. > **Note:** The `HeadOptions` object can also contain a `keywords` property, but Google ignores your site's keywords. 2. Once you’ve created your `HeadOptions` object, pass it to the `ok()` function as the third parameter. This object is then sent to the router page so that Google has SEO information for it. This sample code shows an example of passing the `HeadOptions` object defined above to **myRouter-page**: ```javascript return ok("myRouter-page", data, seoData); ``` ## Step 2 | Create the sitemap for your router To add your router pages to your sitemap, do the following: 1. In your router’s `sitemap()` function, create a [`WixRouterSitemapEntry`](https://dev.wix.com/docs/velo/api-reference/wix-router/wix-router-sitemap-entry.md) object for each URL the router can possibly route to. Use the sample code added to your first `sitemap()` function as a starting point: ```javascript let siteMapEntries = Object.keys(peopleData).map((name) => {     const data = peopleData[name];     let entry = new WixRouterSitemapEntry(name);     entry.pageName = "myRouter-page";     entry.url = "/myRouter/" + name;     entry.title = data.title;     return entry;   }); ``` Each `WixRouterSitemapEntry` in the example includes the router page’s URL, title, and name. The example code groups them into an array. 2. Return all the sitemap entries to create the router sitemap. Again, you can follow the example in the sample code: ```javascript return Promise.resolve(siteMapEntries); ``` ## Step 3 | Check your router sitemap To ensure that your sitemap is working properly, you can get the sitemap from your published site: 1. In a web browser, go to your published site's URL and append `/sitemap.xml` to it. For premium sites, if your site's published URL is: `https://mysite.com`, go to: `https://mysite.com/sitemap.xml`. For free sites, if your site's published URL is `https://username.wixsite.com/site-name`, go to `https://username.wixsite.com/site-name/sitemap.xml`. 2. Check that the sitemap matches the layout of your router pages. ## See also - [Create a router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) - [About routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) - [Tutorial: Create dynamic pages with a custom router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/tutorial-create-dynamic-pages-with-a-custom-router.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Create Dynamic Pages with a Custom Router ## Article: Tutorial: Create Dynamic Pages with a Custom Router ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/tutorial-create-dynamic-pages-with-a-custom-router.md ## Article Content: # Tutorial: Create Dynamic Pages with a Custom Router When you set up [dynamic pages](https://support.wix.com/en/article/cms-about-dynamic-pages) on your site, Wix creates a router in the background to manage the pages. However, you can also choose to set up your own router to manage your dynamic pages. This gives you more control over how incoming requests to your site are handled. The [Create a Router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) article walks you through the default code that appears when you first add a router to your site. In this tutorial, we'll show you how to alter the router code to create and manage dynamic pages yourself. We'll create a very basic site for listing real estate properties. We'll then add a router and walk you through the necessary code to create an index page of listings, and a dynamic item page. In this tutorial we work directly in the editor. However, all of the code and basic website design is relevant if you're working in the Wix IDE or your local IDE. The only differences might be the names and locations of some of the files. If you're having trouble locating some of these files, see our article on [creating a router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md). Before you get started, make sure you're familiar with the concepts of [routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) and dynamic pages. ## Step 1 | Create a new site with a collection and router In this step, we'll create a new site and add the features we need to build dynamic pages. This includes the following: * The collection, which we'll fill with data that will be used to populate the dynamic index and item pages. * The router, which controls the dynamic page that visitors are sent to when they request to see listings on your site. To start the setup: 1. Create a [new blank site](https://wixstudio.new/). 1. On the homepage, add the following 2 elements: - Text element: Edit the text element so its text reads "Welcome to our real estate site". - Button: Click on the button and select **Change Text**. Edit the text to say "Click here to see our listings". ![Change the button text](https://wixmp-833713b177cebf373f611808.wixmp.com/images/05d12620e8ad7a36c9200284ba176a34.png) You can leave the page code empty for now; we'll add code for the homepage later. 1. Now we need to [add a collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) to contain the listings we want to display. We'll query the items in this collection to create our dynamic pages. Name your collection `Real Estate Listings` and make sure it has a matching collection ID `RealEstateListings`. We'll need this ID later to query the collection. Add the following four fields to the collection: - **Title**: Text field that contains the name of the listing. - **Description**: Text field that contains a brief description of the listing. - **Image**: Image field that contains an image of the property in the listing. - **Slug**: Text field that contains the slugified name of the listing. Once you set up your fields, add the following items to the collection exactly as shown in the table: | Title | Description | Image | Slug | | ------| ------------ | ------ | ------- | | Modern urban apartment | This newly built apartment has all the trimmings and appliances you need in the modern-day city. | wix:image://v1/11062b_1c8c953e6532482b974d6b30961091e2~mv2.jpg/Loft%20Style%20Apartment.jpg#originWidth=4000&originHeight=2667 | modern-urban-apartment | | Beach house | Imagine stepping outside your house and you're right on the beach. You can now achieve that dream with this piece of property. | wix:image://v1/d4dde1_954ed2d26fb14166ad634c0299d0ed2c~mv2.jpg/Large%20house%20right%20next%20to%20the%20sea.%20It%20is%20light%20blue%20with%20white%20trim%20and%20doors.%20It%20has%20a%20po#originWidth=500&originHeight=500 | beach-house | | Cute Cottage | This adorable cottage in the suburbs reminds you of old times outside while keeping a modern feel inside. | wix:image://v1/d4dde1_1f312bbf4d8743a695305a3894ac89fb~mv2.jpg/Cottage%20made%20of%20white%20stone%20with%20a%20small%20green%20lawn%20and%20a%20white%20picket%20fence%20with%20a%20gate.%20#originWidth=500&originHeight=500 | cute-cottage | | Cozy Cabin | Nestled deep in the woods, this cabin offers the peace and quiet of the wilderness away from the city. | wix:image://v1/d4dde1_0fe31ec5d1bb4ed184ff61fd6b42ce20~mv2.jpg/A%20wooden%20cabin%20nestled%20in%20between%20trees..jpg#originWidth=500&originHeight=500 | cozy-cabin | 1. [Add a router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) to your site. Set the URL prefix to `listings`. 1. After you create the router, go to the **Backend & Public** section of the code panel and locate the `routers.js` file. It should contain two functions, `listings_Router` and `listings_SiteMap`, with the sample code discussed in [Create a Router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md). In addition, check that a router page called `listings-page` now appears in the code panel. Now that you have created the router, we can start adding code. ## Step 2 | Add dynamic page code In this step, we'll add page code for the dynamic item and index pages to display our property listings. In order to have both an index page and an item page, we need to add two router pages. We'll need to add code to both pages. Let's start by coding the index page, which will display all of the listings for visitor selection: 1. In the **Page Code** section of the code panel, under **Router Pages**, find the router page that was created when you first added the router. It should be called `listings_page`. Click on `listings_page` to open it in the editor. 1. Add a [repeater](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids) to the page. Select an item in the repeater and open the settings menu. Click **Quick Add** to add an image and a title to the repeater item. ![Quick add an element to a repeater](https://wixmp-833713b177cebf373f611808.wixmp.com/images/64bd1ecd4d2e08edf003bf6defa1d387.png) 1. Select the repeater so it's highlighted. In the [Properties & Events Panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md), change the ID of the repeater to `listings`. ![Change the repeater ID to listings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/28380b902072e88aad787e26b0406f80.png) Repeat this with the IDs of the repeater item as well as the title and image that you added. Give the elements the following IDs: - Repeater item: `listing` - Title: `listingTitle` - Image: `listingImage` Now we're ready to add some simple code for our dynamic index page. 1. In the code editor for `listings-page`, add the following code: ```js import { getRouterData } from 'wix-window'; import { to } from 'wix-location'; $w.onReady(function () { const listings = getRouterData(); $w('#listings').onItemReady( ($item, itemData) => { $item('#listingImage').src = itemData.image; $item('#listingTitle').text = itemData.title; $item('#listing').onClick( () => { to(`/listings/${itemData.slug}`); }); } ); $w('#listings').data = listings; }); ``` **Lines 1-2:** Import the `getRouterData()` and `to()` functions. `getRouterData()` receives any data sent by the router so we can render it on the page. **Line 5:** Receive our router data and store it in the `listings` variable for later use. **Lines 7-16:** Populate the repeater items with the router data. We create an `onClick()` handler that sends users to the item page when clicked. Depending on the slug passed, the router controls which dynamic item page is rendered. We have now set up the dynamic index page so that it will display each of the items in the collection. Next, we need to add a second router page that will become our dynamic item page. 1. Under **Router Pages**, click the **More Actions** button next to **Listings Pages (Router)**. Select **Add page to router**. ![Add page to router](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0880f50c08320f50312b121c2003a312.png) Label the new page `listing` and click **Add Page**. This creates a second page under the **Listings Pages (Router)**. 1. Select the `listing` page so it appears in the editor. On a real property listing you'll typically include much more detail, such as price and contact information. However, for this example, we'll keep the dynamic item page very simple and just add a single image, title, and description. Add an image and two text elements to the page, and arrange the elements so the page layout looks like this: ![Dynamic page element layout](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c0c6e41ab94b1bdbbe6de9c39ccf6d9c.png) 1. In the code editor, open the page code for `listing`. We need to write some code that will accept data from the router and use it to render the correct information for the selected property. To do this we can write the following basic code: ```js import { getRouterData } from 'wix-window'; $w.onReady(function () { const listing = getRouterData(); $w('#listingTitle').text = listing.title; $w('#listingDescription').text = listing.description; $w('#listingImage').src = listing.image; }); ``` **Line 1:** Again, we import `getRouterData()` to enable our frontend page to receive data from the router. **Line 4-8:** Get the router data and populate our elements with it. We now have a functioning dynamic index and item page that will render our collection data based on requests to the router. Our next step is to write the router code so that it handles visitor requests correctly. ## Step 3 | Add the router code All the logic for your router goes in the router function added to `routers.js`. In this example, we'll write all our code in the `listings_Router()` function. This is the code that actually handles visitor requests. In the case of dynamic pages, the router logic decides which site page is rendered for the visitor. 1. In the code panel, locate the `routers.js` file in the **Public & Backend** section. The first thing we need to do is import the necessary functions. The sample code already imports several important objects for you from `wix-router`. This includes the `ok()` and `notFound()` functions and the `WixRouterSitemapEntry` object. We'll need to work with the [Wix Data API](https://dev.wix.com/docs/velo/apis/wix-data/introduction.md) to query items from our collection, so we add another line to the import statement: ```js import {ok, notFound, WixRouterSitemapEntry} from "wix-router"; import wixData from "wix-data"; // Add this line ``` The sample code also includes an object with hard-coded data. You can delete this object as we won't use it in this tutorial. 1. In the `listings_Router()` function, delete the sample code; we'll replace it with our own. Recall that we have two router pages, an index page and an item page. The router needs to know when to display one or the other. It can figure this out based on the [path](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/wix-router-request/path.md) variable in the `WixRouterRequest` object that the router receives. The path indicates which item page in our router the visitor wants to view. If the `path` variable is empty, it means the visitor has not requested a specific page in the router, so we render the index page. Therefore we can start by writing an `if` statement that checks the path: ```js export async function listings_Router(request) { if (!request.path[0]) { const { items: listings } = await wixData.query('RealEstateListings').find(); const seoData = { title: 'Property Listings', description: 'List of available properties', noIndex: false }; return ok('listings-page', listings, seoData); } } ``` **Line 2:** Check if `path` is empty. **Lines 3-5:** We query our data collection for all available listings, and store them in an array called `listings`. We pass the ID of our collection, `RealEstateListings`, to the Wix Data `query()` function to retrieve the collection items. **Lines 7-11:** Create our SEO data so Google knows how to find and display our index page in its search. **Line 13:** Call the `ok()` function to go to the index page. We pass it the `listings` data. Recall that we wrote frontend code that will pick up and display this data to the visitor.
__Tip:__ In case you don't know your collection ID, in the sidebar go to **CMS** > **Your Collections**. Open the menu for your collection and click **Edit settings**. The modal that pops up contains some information about your collection including its ID.
1. Now we need to handle the case where the `path` variable is _not_ empty. There are two cases here: - The visitor requests an existing path. - The visitor requests a path that doesn't exist. Let's start with the case where the requested path exists. Add the following `else` statement to the `if` we just wrote: ```js else { const results = await wixData.query('RealEstateListings') .eq('slug', request.path[0]) .find(); if (results) { const listing = results.items[0]; const seoData = { title: listing.title, metaTags: [ { name: 'description', content: listing.description }, { name: 'og:title', content: listing.title }, { property: 'og:image', content: listing.image } ], }; return ok('listing', listing, seoData); } } ``` **Lines 2-4:** We again query the collection, but this time we filter the data by searching for a slug that matches one in the collection. **Lines 6-8:** If we get a result, we pull the item data out and place it in a variable `listing`. **Lines 10-24:** We use the data from `listing` to populate the SEO object. **Line 26:** We pass the listing and SEO data to the `ok()` function to render the correct page to the visitor. To handle the case of a request for a non-existing path, we only need to make one simple addition: ```js else { const results = await wixData.query('RealEstateListings') .eq('slug', request.path[0]) .find(); if (results) { const listing = results.items[0]; const seoData = { title: listing.title, metaTags: [ { name: 'description', content: listing.description }, { name: 'og:title', content: listing.title }, { property: 'og:image', content: listing.image } ], }; return ok('listing', listing, seoData); } else { // Add this to handle requests for items that don't exist return notFound(); } } ``` **Line 28:** Send the requester to an error page if we don't find the slug in our collection. This completes the code for our router function. Our router now knows how to handle requests for the index page and the dynamic item pages. ## Step 4 | Add the sitemap code The sitemap is necessary for your site's SEO. When you use dynamic pages, Wix handles the SEO and site mapping for you. But when you manually create dynamic pages with your own router, you must build your own sitemap using the provided function. The sitemap function also appears in `routers.js`, immediately below the related router function. In this tutorial, the sitemap function is called `listings_SiteMap()`. 1. Delete the sample code in `listings_SiteMap()` and add the following code: ```js export async function listings_SiteMap(sitemapRequest) { const { items: listings } = await wixData.query('RealEstateListings').find(); const siteMapEntries = listings.map(listing => { const entry = new WixRouterSitemapEntry(listing.title); entry.pageName = 'listing'; entry.url = '/listings/' + listing.slug; entry.title = listing.title; return entry; }); const indexEntry = new WixRouterSitemapEntry('Property Listings'); indexEntry.pageName = 'listings-page'; indexEntry.url = '/listings' indexEntry.title = 'Property Listings'; siteMapEntries.push(indexEntry); return siteMapEntries; } ``` **Line 2:** We query the collection to get the item data, and store the data for each item in an object `listings`. **Line 4-10:** We create a new site map entry for each item in the collection and map it to a new array `siteMapEntries`. We set the sitemap entry title from the title of the listing, and use the item's slug to create the entry's URL. **Lines 12-17:** We also need to add the index page to the site map, so we create a separate sitemap entry and push it to the `siteMapEntries` array. **Line 19:** Return the sitemap. We've created the sitemap, and now have a fully functional router that works with SEO. Here's the full code of `router.js`, including the router and sitemap functions: ```js import {ok, notFound, WixRouterSitemapEntry} from "wix-router"; import wixData from "wix-data"; export async function listings_Router(request) { // If the request doesn't have a path attached, render the index page if (!request.path[0]) { const { items: listings } = await wixData.query('RealEstateListings').find(); const seoData = { title: 'Property Listings', description: 'List of available properties', noIndex: false }; return ok('listings-page', listings, seoData); } else { const results = await wixData.query('RealEstateListings') .eq('slug', request.path[0]) .find(); if (results) { const listing = results.items[0]; const seoData = { title: listing.title, metaTags: [{ name: 'description', content: listing.description }, { name: 'og:title', content: listing.title }, { property: 'og:image', content: listing.image }], }; return ok('listing', listing, seoData); } else { return notFound(); } } } export async function listings_SiteMap(sitemapRequest) { const { items: listings } = await wixData.query('RealEstateListings').find(); // Convert the data to site map entries const siteMapEntries = listings.map(listing => { const entry = new WixRouterSitemapEntry(listing.title); entry.pageName = 'listing'; // The name of the page in the Wix editor to render entry.url = '/listings/' + listing.slug; // Relative URL of the page entry.title = listing.title; // For better SEO - Help Google return entry; }); const indexEntry = new WixRouterSitemapEntry('Property Listings'); indexEntry.pageName = 'listings-page'; // The name of the page in the Wix editor to render indexEntry.url = '/listings' // Relative URL of the page indexEntry.title = 'Property Listings'; // For better SEO - Help Google siteMapEntries.push(indexEntry); return siteMapEntries; } ``` ## Step 5 | Test dynamic pages 1. Publish your site by clicking **Publish** in the top right corner of the editor. 1. Open the menu in the top left corner and click **Site** > **View Published Site**. ![View published site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/288053bdb3f77fb88c6199a9ed0e7214.png) The published site opens in a new tab. You should see the homepage first: ![Router tutorial starting page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1d1cd520ed4477d14aac097973bb6845.png) 1. Click the button on the homepage to go to the listings index page. Because you're requesting to view a page with the `listings` URL prefix, Wix passes the request to your router to handle. The site should navigate to the dynamic index page you created. From there, click on any of the listings on the page. The site then navigates to the dynamic item page, correctly rendered for the item you selected. Again, this request is handled by the router. ![Dynamic item page display](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bfa074e998490f6b3c9470c340756739.png) 1. You can return to the index page and test out the other listings if you wish. Each time you click on a listing, you make a request to the router and it fetches the item data from the `Real Estate Listings` collection, and renders it on the item page. This is the same thing Wix does when you add dynamic pages to your site! 1. Now that you know your router is working, let's take a brief look at the sitemap. Return to your homepage and in the address bar, add `/sitemap.xml` to your site address. It looks something like this: ```bash https://mysite.wixstudio.io/test-site/sitemap.xml ``` 1. Navigate to the address and you'll see your site's sitemap displayed in XML. For example: ```xml https://mysite.wixstudio.io/test-site/dynamic-listings-sitemap.xml 2024-10-14 https://mysite.wixstudio.io/test-site/pages-sitemap.xml 2024-10-14 ``` One of the links leads to your router pages. Copy the link and paste it back into the address bar. Navigate to the link to see the sitemap for your router: ```xml https://mysite.wixstudio.io/test-site/listings/cozy-cabin 2024-10-14 https://mysite.wixstudio.io/test-site/listings/beach-house 2024-10-14 https://mysite.wixstudio.io/test-site/listings/modern-urban-apartment 2024-10-14 https://mysite.wixstudio.io/test-site/listings 2024-10-14 https://mysite.wixstudio.io/test-site/listings/cute-cottage 2024-10-14 ``` This is the sitemap generated by your `listings_SiteMap()` function. In this tutorial, you've created your own router and used it to set up dynamic pages on your site manually. From here you can enhance the site design and adjust it to your needs, and continue building on the router so it can handle more complex tasks. ## See also * [About routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) * [Create a router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) * [About SEO and routing](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Premium Plans ## Article: About Premium Plans ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md ## Article Content: # About Premium Plans Wix allows you to extend your site's functionality and customize it to fit your needs. You can handle your site's data using the [Wix Data APIs](https://www.wix.com/velo/reference/wix-data) and [CMS (Content Management System)](https://support.wix.com/en/article/about-the-content-manager-7160473) collections. You can also add custom code to your site using [Velo](https://support.wix.com/en/article/about-velo-by-wix). Different sites require different amounts of resources, depending on the complexity and volume of their code and data use. Wix offers a number of premium plans that include different levels of data, computing, and developer features. This way, you can choose exactly what you need for your site, depending on its complexity and specific needs. To better understand what premium features are available, check out: * [Data platform](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) * [Compute platform](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) * [Developer tools](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/developer-tools-features.md) [Compare](https://www.wix.com/upgrade/website) the resources included in each plan and find the one that's right for you. You can also try out many of the premium features for free. Find out more about [Using Velo with a Free Wix Site](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/using-velo-with-a-free-wix-site.md). > **Note:** Premium plans added to sites before September 2023 are now considered legacy plans. If you have a site with a legacy plan, your site's resource limits won't change. Learn more about [legacy plan limits](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/legacy-data-compute-and-developer-tools-limits.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Legacy Data, Compute, and Developer Tools Limits ## Article: Legacy Data, Compute, and Developer Tools Limits ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/legacy-data-compute-and-developer-tools-limits.md ## Article Content: # Legacy Data, Compute, and Developer Tools Limits In September 2023, Wix launched new premium plans that include updated limits on data, compute platform, and developer tools features. The new limits impact existing sites, as follows: * Existing free sites moved to the new [free tier limits](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/using-velo-with-a-free-wix-site.md). * Existing sites with earlier premium plans continue receiving [legacy plan limits](https://support.wix.com/en/article/legacy-data-compute-and-developers-tools-limits#legacy-plan-limits). ## Checking your site's plan If your site has a premium plan, you can check if it has a legacy premium plan or a new premium plan. To check your plan type, do the following: 1. Open your site's [dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fhome). 2. Navigate to **Settings** > **Manage plan**. 3. Click **View Plan Features**. ![](https://github.com/wix-incubator/wix-code-docs/assets/89579857/cf306eea-c71f-4349-8618-00fa13e2ab49) A modal opens, displaying your site's premium plan features. 4. Scroll down to see your site's plan limits. If you see dedicated sections for **Data platform**, **Compute platform**, and **Developer tools**, your site has a new premium plan. If you don't see these sections, your site has a legacy premium plan. ![](https://github.com/wix-incubator/wix-code-docs/assets/89579857/f780c659-6c95-4400-9e34-a2a0dcdf8dd8) ## Changing your site's plan If your site has a legacy premium plan and you move to new plan, your plan's limits change. In some cases, the new plan may have lower limits for some features than your site's current usage. Downgrade behavior varies by feature. Learn more about downgrade behavior for [data platform](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md), [compute platform](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md), and [developer tools](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/developer-tools-features.md) features. ## Legacy plan limits These tables list the limits for legacy premium plans. ### Data platform |Feature |Limit | |---|---| |Data storage|10 GB |Data collections|1000 |Data indexes|\- |Data requests per minute|Read: 3000/minute
Write: 1500/minute |Data request timeout|5 seconds |External database integration|Yes ### Compute platform |Feature |Limit | |---|---| |Backend containers| 1 micro container |Capacity per container|1 vCPU
400 MB RAM |Backend requests per minute|7000 |Backend request timeout|14 seconds ### Developer tools |Feature |Limit | |---|---| |Scheduled jobs|Up to 20
1 hour minimum interval |Backend event handlers|Yes |Service plugins|Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using Velo with a Free Wix Site ## Article: Using Velo with a Free Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/using-velo-with-a-free-wix-site.md ## Article Content: # Using Velo with a Free Wix Site You can try out most Velo and Data features with a [free site](https://support.wix.com/en/article/building-a-website-for-free). As you develop your site you may reach some of the free site quotas, and can select a [plan](https://www.wix.com/upgrade/website) to increase the number of features and resources available for your site. >**Note:** Some Velo and data quotas are not enforced on all sites yet. The following table lists the feature and resource quotas available for free sites. You can learn more about each feature [here](https://support.wix.com/en/article/velo-about-premium-packages). |Feature|Free Site Limit| |--|--| |**Data Platform**| | |Dedicated infrastructure|Not available |Data storage|1,000 items |Data collections|1,000 |Data indexes|4 |Data requests per minute|1000 reads
60 writes |Data request timeout|5 seconds |**Compute platform**|| |Backend containers| 1 micro |Capacity per container|1 vCPU
400 MB RAM |Backend requests per minute|60 |Backend request timeout|14 seconds |**Developer tools**|| |Scheduled jobs|20 jobs
1-hour minimum interval --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Data Features ## Article: Data Platform Features ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md ## Article Content: # Data Features All sites have different data capacities and resource needs. Data quotas apply to anything making data requests on a site, including the [CMS (Content Management System)](https://support.wix.com/en/article/about-the-content-manager-7160473), [Datasets](https://support.wix.com/en/article/about-datasets-6368396), and the Velo [Wix Data API](https://www.wix.com/velo/reference/wix-data). This article covers the data platform features that are available based on your [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Learn about the differences and [choose the plan](https://www.wix.com/upgrade/website) that's right for you. ## Data storage A site's collections and items are hosted by Wix, and there is a limit to the amount of data you can store on a site. This limit only applies to your collection items. ### Collection items Wix limits the total number of collection items a site can have. If a site reaches this limit, you won't be able to add any more data to any of your collections. The collection items limit is not applied to [Wix app collections](https://support.wix.com/en/article/content-manager-working-with-wix-app-collections) or [external database collections](https://support.wix.com/en/article/velo-adding-and-deleting-an-external-database-collection) connected to a site. It also doesn't apply to items in [sandbox collections](https://support.wix.com/en/article/cms-about-sandbox-and-live-collections-and-syncing). Collection items can also include media or other files, which have [separate limits](https://support.wix.com/en/article/about-storage-and-bandwidth).
**Notes**: - If a site exceeds its storage quota and you delete data to free up space, it may take up to 1 hour before you can add new data to your collections. - There are also some [limits](https://support.wix.com/en/article/field-type-support-and-limitations-in-the-content-manager) that are specific to certain field types. - Collections in Blocks apps also affect a [site's collection item limit](https://support.wix.com/en/article/wix-studio-cms-limits-on-free-sites) depending if the app is private or public: - **Private Blocks apps**: Items in collections from private Blocks apps count towards the site's collection item quota. - **Public Blocks apps**: Items in collections from public Blocks apps, installed from the Wix App Market, don't count towards the site's collection item quota.
Downgrade behavior If you downgrade to a lower tier plan, the storage quota changes to the one defined by the new plan. If your site has more than this amount of data when you downgrade, none of your data is deleted. However, you can't add any new data to your site until you delete enough existing data to be below the quota.
### Optimize your data storage To avoid reaching the data storage limits, read about how to [optimize your data storage](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md#storage). You can also [upgrade your plan](https://www.wix.com/upgrade/website) to receive a larger storage limit. ## Data requests Every time you access your data, whether that's to read or to write it, you are making a data request. Wix limits the frequency of your requests and the amount of time a request can take. ### Requests per minute Wix limits the number of data requests your site can make per minute. Once a site reaches this quota, subsequent requests during the same minute are not processed and return an error. There are separate quotas for read requests and write requests. The following API functions count as 1 request towards the read quota: - [`get`](https://www.wix.com/velo/reference/wix-data/get) - [`find`](https://www.wix.com/velo/reference/wix-data/wixdataquery/find) - [`count`](https://www.wix.com/velo/reference/wix-data/wixdataquery/count) - [`distinct`](https://www.wix.com/velo/reference/wix-data/wixdataquery/distinct) - [`aggregate`](https://www.wix.com/velo/reference/wix-data/aggregate) - [`isReferenced`](https://www.wix.com/velo/reference/wix-data/isreferenced) - [`queryReferenced`](https://www.wix.com/velo/reference/wix-data/queryreferenced) - [`include`](https://www.wix.com/velo/reference/wix-data/wixdataquery/include) - [`next`](https://www.wix.com/velo/reference/wix-data/wixdataqueryreferencedresult/next) - [`refresh`](https://www.wix.com/velo/reference/wix-data/wixdataqueryreferencedresult/next) - [`getItems`](https://www.wix.com/velo/reference/wix-dataset/dynamicdataset/getitems) (For newly requested items that are not currently in your cache.) The following API functions count as 1 request towards the write quota: - [`insert`](https://www.wix.com/velo/reference/wix-data/insert) - [`bulkInsert`](https://www.wix.com/velo/reference/wix-data/bulkinsert) - [`update`](https://www.wix.com/velo/reference/wix-data/update) - [`bulkUpdate`](https://www.wix.com/velo/reference/wix-data/bulkupdate) - [`save`](https://www.wix.com/velo/reference/wix-data/save) - [`bulkSave`](https://www.wix.com/velo/reference/wix-data/bulksave) - [`remove`](https://www.wix.com/velo/reference/wix-data/remove) - [`bulkRemove`](https://www.wix.com/velo/reference/wix-data/bulkremove) - [`insertReference`](https://www.wix.com/velo/reference/wix-data/insertreference) - [`removeReference`](https://www.wix.com/velo/reference/wix-data/removereference) - [`replaceReferences`](https://www.wix.com/velo/reference/wix-data/replacereferences) > **Note:** Each use of [include](https://www.wix.com/velo/reference/wix-data/wixdataquery/include) in a query counts towards the request quota. For example, the following query counts as 3 read requests: ```javascript wixData.query("Movies").include("director").include("studio").find(); ``` For each request that exceeds the quota, the request fails and the following error is displayed in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md): `WDE0014: Requests per minute quota exceeded.` Learn more about [optimizing your data requests](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md#data-requests) and [Wix Data error codes](https://dev.wix.com/docs/velo/apis/wix-data/error-codes.md). ### Request payload limits Wix Data limits the size of the request payloads. Single-item requests, such as [`update()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/update.md) or [`insert()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/insert.md), have a payload limit of 512 KB. Exceeding this limit results in a `WDE0009` error. Bulk operations, such as [`bulkUpdate()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/bulk-update.md) or [`bulkInsert()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/bulk-insert.md), have a total payload limit of 4 MB. Exceeding this limit results in a `WDE0109` error. Learn more about [Wix Data error codes](https://dev.wix.com/docs/sdk/backend-modules/data/wix-data-error-codes.md). ### Request timeout When your site makes a data request, it may take some time to receive a response. Wix Data places limits on how long a response can take before the request times out. If the response time exceeds this limit, Wix Data returns an error instead of the intended result. Request timeouts vary depending on your [Wix plan](https://www.wix.com/plans) and the type of collection you are attempting to access: | Collection Type | Wix plan | Timeout | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ---------- | | [CMS collections](https://support.wix.com/en/article/cms-formerly-content-manager-managing-your-collections) | Free sites, premium sites (excl. Elite and Business Elites) | 5 seconds | | [CMS collections](https://support.wix.com/en/article/cms-formerly-content-manager-managing-your-collections) | Business Elite & Elite Premium | 10 seconds | | [External Database Collections](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md) | All plans | 15 seconds | After the specified timeout, the request fails and the following error message is displayed in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md): `WDE0028: Operation time limit exceeded.` Learn more about [Wix Data error codes](https://dev.wix.com/docs/velo/apis/wix-data/error-codes.md). ### Blocks apps and request limits When a Blocks app is installed on a site, whether a private or a public app, data requests made by the app are subject to the site's requests per minute (RPM) limits, not the app's limits. ### Optimize your requests To avoid reaching the limits for data requests, read about how to [optimize your requests](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md#data-requests). You can also [upgrade your plan](https://www.wix.com/upgrade/website) to receive larger limits for your data requests. ## Data management It's important to understand your database's infrastructure, how it affects data management, and how to use indexes to improve data requests. ### Database structure [Database collections](https://support.wix.com/en/article/content-manager-about-your-collection-fields) store dynamic content for a site, such as the content that displays on sites and site visitor generated content. Database collections for free and most premium sites are stored in large, multi-tenant databases. This doesn't mean that a site's data is exposed. It does mean that there are limits on the storage, read/write requests, and indexing resources available to a site. Database collections for sites with the Business Elite plan are stored in dedicated databases, providing them with significantly greater data resources. ### Data indexes A [database collection index](https://support.wix.com/en/article/velo-indexes-and-wix-data-collections) is a set of keys used to improve performance when searching for data in a database. Indexes can be created on a single collection field or multiple fields. Indexes are added to fields or sets of fields that are used most often when querying the collection to retrieve information. Collections can have 2 types of indexes: - **Regular:** These indexes improve retrieval performance as described above. - **Unique:** These are special indexes that improve performance and also enforce the uniqueness of values in the field that they're defined for. The Business Elite plan allows you to create a combination of unique or regular indexes up to the amount of indexes in the quota. Other plans include only 3 regular indexes and 1 unique index.
Downgrade behavior If you downgrade to a lower tier plan, the oldest indexes are maintained up to the new plan's quota. All other indexes on the site are deleted. If you downgrade to a free plan, all indexes are deleted.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Compute Features ## Article: Compute Platform Features ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md ## Article Content: # Compute Features Improve your site's performance and handle more backend requests with a compute platform [upgrade](https://www.wix.com/upgrade/website) to your [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Check out the feature differences below. ## Backend containers Velo backend code for Wix sites is run in containers. A container is a package of software containing everything that is needed for its code to run in any environment. You can think about it as a virtual computer being used to run the site’s code. Containers are limited by the amount of computing resources that are allotted for them. There are 2 types of containers available for Wix sites: * **Micro containers:** Include 1 vCPU for processing and 400 MB of RAM. * **Standard containers:** Include 2 vCPUs for processing and 600 MB of RAM. Higher tier premium plans include more containers. If your site needs to perform a lot of operations that require large amounts of processing or memory, it may benefit from a plan that includes more containers. ## Backend requests Wix limits the frequency of your backend requests and the amount of time a request can take. The following section will explain how. ### Backend requests per minute Wix limits the number of requests that can be made from your site's frontend code to its backend code. Once the quota is reached, any backend requests sent for the rest of the minute are not processed. If your site exceeds the quota, any additional requests within a 1-minute window are blocked and you receive a 429 response code. For each throttled request, the following error message is displayed in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md): `/backend/.js(w)/ was throttled because your site exceeded the maximum number of backend requests per minute.` The failed request also is displayed on your site’s [monitoring dashboard](https://support.wix.com/en/article/velo-monitoring#understand-the-dashboard). ### Backend request timeouts When frontend code on a site makes requests to backend code, a request is made to a Wix server. Wix places limits on how long the code on the server can run before it times out. If it takes longer, you receive a 504 response code. If your execution exceeds the quota, the code might still execute, but the connection to the client is closed, so the results do not appear in the frontend. The following error message is displayed in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md): `/backend/.js(w)/ timed out because it exceeded the maximum execution time.` The failed request also is displayed on your site’s [monitoring dashboard](https://support.wix.com/en/article/velo-monitoring#understand-the-dashboard). ### Optimize your requests To avoid reaching the limits for data requests, read about how to [optimize your requests](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md#backend-requests). You can also [upgrade your plan](https://www.wix.com/upgrade/website) to receive larger limits for your data requests. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Developer Tools Features ## Article: Developer Tools Features ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/developer-tools-features.md ## Article Content: # Developer Tools Features You can customize your site and analyze its usage with advanced developer tools. Find the [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md) that suits your needs and [choose the plan](https://www.wix.com/upgrade/website) that fits. ## Scheduled jobs You can use Velo to [schedule code](https://support.wix.com/en/article/velo-scheduling-recurring-jobs) to run on a site at regular intervals. Each scheduled function is called a job. Scheduling jobs allows certain site functions to be automated such as updating or clearing database collections and sending status reports. Wix limits the number of jobs you can define for a site and how frequently jobs can run. Most plans allow you to add up to 20 scheduled jobs that run at a minimum of 1 hour intervals. Elite and Business Elite plans allow you to add up to 30 scheduled jobs that run at a minimum of 5 minute intervals.
Downgrade behavior If you downgrade to a lower tier plan and you have more jobs on your site than the new plan allows, some of your jobs may not run.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Optimizing Your Code ## Article: Optimizing Your Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md ## Article Content: # Optimizing Your Code You may want to optimize your code to avoid reaching the limits for [data storage](#storage), [data requests](#data-requests), or [backend requests](#backend-requests). You can also [upgrade your plan](https://www.wix.com/upgrade/website) to receive more resources. Read [about premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md) to understand the limitations that exist and which plans increase which limits. ## Storage You can take the following steps to reduce the amount of [storage](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) your site uses: * **Delete data you don’t need:** Make sure your collections contain only data that you need for your site. * **Use an external database:** The storage quota doesn't apply to external databases. If none of the other options work for you, store your data in an external database and [integrate it with your site](https://support.wix.com/en/article/velo-adding-and-deleting-an-external-database-collection). You can still access this data using Velo's [Wix Data API](https://www.wix.com/velo/reference/wix-data-v2). ## Data requests You can optimize your data requests by reducing the number of requests you make or by decreasing the requests' processing time. ### Amount of requests You can take the following steps to reduce the number of [data requests](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) in your code: * **Use the bulk functions:** If you are inserting or updating multiple items, use the `bulkInsert()` and `bulkUpdate()` functions instead of writing each item individually. These functions count as 1 request toward the quota. * **Make requests only when you need the data:** Don’t make requests for data you don’t use. For example, if you store extra product data in a separate collection, query this collection only when you need the data. ### Processing time You can take the following steps to reduce the [processing time](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) of the data requests in your code: * **Request only data you need:** Avoid fetching more items from your collection than you need at once. Also avoid including reference items in your queries if you don’t need their data. * **Use indexes:** Querying with an index is faster than querying items directly. All `_id` and `_createdDate` fields are indexed by default. * **Process data in chunks:** Timeouts can happen when you process too much data with one request. Breaking your request into smaller pieces means that each individual request will complete before the time limit. If you are running a slow process or loading a large amount of data, [use pagination](https://support.wix.com/en/article/adding-and-setting-up-a-pagination-bar) or [process your data in chunks](https://support.wix.com/en/article/velo-best-practices-for-improving-performance-in-wix-sites-with-data). Don't create too many requests, as there is also a [quota on requests per minute](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md). * **Use an external database:** The request timeout and processing time quotas don't apply to external databases. If none of the other options works for you, store your data in an external database and [integrate it with your site](https://support.wix.com/en/article/velo-adding-and-deleting-an-external-database-collection). You can still access this data using Velo and the Wix Data API. ## Backend requests You can optimize your backend requests by reducing the number of requests you make or by decreasing the requests' processing time. ### Amount of requests You can take the following steps to reduce the number of [backend requests](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) in your code: * **Batch your requests:** Batch multiple backend requests into one. Make sure not to batch time-consuming requests together as there's also a [time limit](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) for backend requests.
See an example Instead of calling a series of backend functions from your frontend code, like this: ```js import {getSalary, getTax, calculateFinalSalary} from 'backend/myBackend.jsw'; const salary = await getSalary(employeeId); const tax = await getTax(employeeId); const finalSalary = await calculateFinalSalary(salary, tax); ``` You can batch the requests together in the backend and make one call to the batch function from the frontend, like this:
**Frontend code** ```js import {calculateFinalSalary} from 'backend/myBackend.jsw'; const finalSalary = await computeFinalSalary(employeeId); ``` **Backend code** ```js export function computeFinalSalary(employeeId) { const salary = getSalary(employeeId); const tax = getTax(employeeId); return calculateFinalSalary(salary, tax); } ```
* **Avoid repetition:** Avoid repeating large database requests or complex calculations. If you can, [cache the result](https://support.wix.com/en/article/site-performance-caching-pages-to-optimize-loading-speed) or [aggregate database queries](https://support.wix.com/en/article/velo-working-with-aggregations-in-the-data-api). ### Processing time You can take the following steps to reduce the [processing time](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) of the backend requests in your code: * **Identify parts of your code that are running for too long:** Identify the parts of your code that are taking too long to run, and optimize them to improve performance. You can do this by including console logs before and after the code you want to test, and then checking to see how long it takes to run.
See an example To test your code’s running time using console logs, do the following: 1. In your backend code, add [`console.time()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) before calling a function and [console.timeEnd()](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd) after the call. For example: ```js export function rocketModel () { console.time(); const trajectory = calculateTrajectory(velocity, direction); console.timeEnd(); } ``` 2. Open [Wix Logs](https://support.wix.com/en/article/velo-viewing-live-site-monitoring-events) log. 3. Use [Functional Testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) to trigger your backend code. 4. Check the log to see how long it took your function to run. The results are displayed in your **Wix Logs** window: ![wix logs](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/01/01/95fb6ee8-f12b-4764-9f95-09aed6ba25e7/02319b66-272f-4932-8165-4031bac9b10b.png)
* **Check your 3rd-party APIs:** If you are using 3rd-party APIs on your site, make sure they are not too slow. For example, if you notice an API call to a 3rd-party is taking a few seconds to run, break it into smaller calls or try upgrading your 3rd-party account to improve performance. * **Process data in chunks:** If you are running a slow process or loading a large amount of data, [use pagination](https://support.wix.com/en/article/adding-and-setting-up-a-pagination-bar) or [process your data in chunks](https://support.wix.com/en/article/velo-best-practices-for-improving-performance-in-wix-sites-with-data). Don't create too many requests, as there is also a [quota on requests per minute](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md). * **Use indexes:** Querying with an index is faster than querying items directly. All **\_id** and **\_createdDate** fields are indexed by default. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Actions ## Article: About Custom Actions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-actions.md ## Article Content: # About Custom Actions When you create a new [automation](https://support.wix.com/en/article/wix-automations-about-the-new-automations-builder) on your site, you're presented with a variety of actions to execute after your automation is triggered. The actions available to you depend on the apps that are installed on your site. However, if none of the available actions execute the behavior you want, you also have the option to create your own custom action. This action is independent of any app and applies only to the site it's created on. You can create custom actions with the [Automations Action service plugin](https://dev.wix.com/docs/velo/events-service-plugins/automations/service-plugins/automations-actions/introduction.md). The service plugin lets you code a custom action directly in the automation builder. Examples of actions you can create with the Action service plugin include: - Create custom email notifications for specific events. - Trigger third-party APIs when a subscriber signs up on your site. - Log specific actions into a custom database for business analysis. ## Supported IDEs You can implement custom actions using: - The **code editor** (Wix Studio and Wix Editor). - The Wix IDE (Wix Studio). ## Custom action code To [create a custom action](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/add-a-custom-action.md), you select the **Run Velo code** action in the automation builder. Wix creates a Javascript file where you write your action code. You then edit this file in a modal inside the builder. Wix automatically populates the JS file with the following code: ```js /** * Autocomplete function declaration, do not delete * @param {import('./__schema__.js').Payload} options */ export const invoke = async ({payload}) => { return {} // The function must return an empty object, do not delete }; ``` You must write all your code inside the [`invoke()`](https://dev.wix.com/docs/velo/events-service-plugins/automations/service-plugins/automations-actions/invoke.md) method. Wix calls this method each time the automation is triggered, and runs any code inside. It expects an empty object in return, so make sure to leave the return statement as it appears. To write your action code, use the Wix APIs just like you would in your site's public or backend files. When you create a custom action for the first time, Wix adds the Automations Action service plugin to your site's backend code files. Under the Action service plugin, Wix adds a folder for each subsequent custom action you create. This folder contains a copy of the action code you write in the builder. However, any changes you make in the backend file aren't reflected when the automation runs. Write and edit your action code only in the automation builder. Working directly in the automation builder also allows you to: - Easily verify you are working with the correct trigger. - View the full trigger payload. - Edit sample data provided by Wix and test your code with it. ## Payload data Depending on the trigger you select, your action expects to receive a payload when the automation is triggered. You can view the payload structure under the **Payload view** tab while writing your code in the builder. The structure is read-only. You can access payload data in your code with dot notation. For example: ```js /** * Autocomplete function declaration, do not delete * @param {import('./__schema__.js').Payload} options */ export const invoke = async ({payload}) => { console.log(payload.status) return {} // The function must return an empty object, do not delete }; ``` ## Editing the automation To edit your action code, return to the automation in the builder and make your changes directly in the custom action modal. The code file you create for your custom action is associated with the original trigger you selected. Editing the trigger configuration doesn't affect the custom action. However, if you change the trigger, a new code file will be created and you'll have to write new code. The initial file you create is still saved, and if you restore the trigger it is associated with, you'll be able to access it again in the automation. ## See also - [Create a Custom Action](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/add-a-custom-action.md) - [Automations Actions API Reference](https://dev.wix.com/docs/velo/events-service-plugins/automations/service-plugins/automations-actions/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add a Custom Action ## Article: Add a Custom Action ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/add-a-custom-action.md ## Article Content: # Add a Custom Action The Automations Action service plugin lets you develop [custom actions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-actions.md) directly in your site dashboard. To use a custom action as part of an automation, start by creating a new automation in your site dashboard: 1. Go to the [Automations](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Ftriggers) page in your dashboard. 2. Click **New Automation**, then **Start from Scratch** to open the automation builder. 3. Select and configure a [trigger](https://support.wix.com/en/article/wix-automations-creating-an-automation-with-the-new-builder#step-2-choose-a-trigger). 4. Click the **+** to add a step and choose **Action**. In the menu on the left, select **Run Velo code**. ![Run velo code action](https://wixmp-833713b177cebf373f611808.wixmp.com/images/34472349eca2250ac5c157eaf040f2c5.png) 5. Click **Start Coding** to add your code. You'll be prompted to create a new code file. ![Start coding](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ebb182e0763598c9a44bfb79d20a5e04.png) 6. Give the file a name and click **Create & Start Coding**. ![Name file and start coding](https://wixmp-833713b177cebf373f611808.wixmp.com/images/05a191f115d4928877886b5c531708e0.png) You should now see something like this: ![Write velo action code](https://static.wixstatic.com/media/d4dde1_636f1a1ad55c4c0399410db29c86912b~mv2.png) The **Write Velo Code** modal contains two tabs. The first is the .js file where you write your code. The second tab, which appears under **Payload view**, lets you view the expected structure of the trigger payload. 7. When you finish writing your code, click **Save**. Now that you have set up and coded your custom action, you can activate the automation immediately. However, we recommend testing the action code first. 8. To test your code, follow the instructions under **Run your code to test this action**, then click **Run Code**. ![Test your action code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e0961f5637cd9a5047dfc671fc8021b0.png) Wix provides sample data that matches the structure of your trigger payload. You can edit any of the fields in the sample payload to test your action. Be aware that testing the action runs the code live on your site, and may change cause changes in the site or dashboard. Once you test your code and are sure your action works, activate your automation. ## Edit your custom action To edit your action code, [edit your automation](https://support.wix.com/en/article/the-new-automation-builder-managing-your-automations#editing-duplicating-or-renaming-an-automation) and select the **Run Velo code** action, then click on **Edit Code**. Make the changes you want, save the code, and test. ## See also - [About Custom Actions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-actions.md) - [Automations Actions service plugin](https://dev.wix.com/docs/velo/events-service-plugins/automations/service-plugins/automations-actions/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Triggers ## Article: About Custom Triggers ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-triggers.md ## Article Content: # About Custom Triggers When you create an [automation](https://support.wix.com/en/article/wix-automations-about-the-new-automations-builder) on your site, you must select a trigger that causes the automation to run. A trigger is an event that occurs on your site and gets reported to Wix. An automation only runs when the event that defines its trigger occurs. For example, if you select the **Member logs in** trigger, your automation will run when a member logs in. Most triggers are based on similar business logic, like payment information or contact management. However, you may want to run your automation when an event occurs that is not defined by existing triggers. Examples include: - Creating a new contact when a site visitor clicks a certain button. - Send a chat message offering help when a member inputs an invalid value into a text input field. - Send a coupon if a visitor is on your site for a certain period of time. With the [Custom Trigger API](https://dev.wix.com/docs/sdk/backend-modules/automations/triggers/custom-trigger/introduction.md), you can run an automation for any event that occurs by triggering the automation from your code. This gives you greater control and flexibility over when your automations run. ## Supported IDEs Once you select the code trigger in the automation builder, you can write your code in the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor). ## Trigger configuration There are 2 parts to the trigger configuration: ### The trigger ID When you select the **Velo code trigger** as your automation trigger, Wix generates a unique trigger ID. When you call [Run Trigger](https://dev.wix.com/docs/sdk/backend-modules/automations/triggers/custom-trigger/run-trigger.md), you pass it the trigger ID. The method then runs the automation associated with the ID. The result is that you can run the automation from anywhere in your site code, simply by calling Run Trigger with the relevant ID. ### The payload schema In addition to the ID, you can optionally define a payload schema for the trigger, and pass a payload to Run Trigger. The structure of the payload schema is generated from sample data you enter when you configure the trigger. For example, if you enter sample data like this: ```javascript {  "firstName": "Ronald",  "id": "ed8fa327-f821-4c30-b336-1784be278541",  "age": 50,  "contactEmail": "ronald@example.com",  "favoriteFoods": ["hamburgers", "French toast", "chicken nuggets"] } ``` Your payload schema will have a structure like this: ```javascript {   firstName: string,   id: string (uuid),   age: number,   contactEmail: string (email),   favoriteFoods: [ string ] } ``` Payload fields are optional, so even if you define a payload schema, you’re not required to pass a payload when you call Run Trigger. If you choose to pass a payload, you can omit fields. Any fields you include in the payload should match the key and type defined in the schema. For example, if your payload structure looks like the one above, you can pass the following payloads to Run Trigger: ```javascript $w('#myButton1').onClick(async () => { const payload = { firstName: "Amy", id: "5d696ec4-0933-4b4d-9acd-50b316717ff2", age: 27, contactEmail: "amy@email.com", favoriteFoods: ["pizza","hamburger"] }; await runTrigger(payload); }) $w('#myButton2').onClick(async () => { const payload = { firstName: "Josh", id: "46123fae-6b6f-4edb-98f1-50f0eaded97e", contactEmail: "josh@email.com", }; await runTrigger(payload); }) $w('#myButton3').onClick(async () => { const payload = { firstName: "Jackie", favoriteFoods: ["sushi", "tiramisu"] }; await runTrigger(payload); }) ``` Make sure, however, that any fields required by actions are passed. For example, if subsequent actions need to access contact data, be sure to pass the contact ID field. #### Contact data in the payload schema If your payload schema contains one or more UUID fields, you can mark one of those fields as a contact ID. When the payload is passed to Run Trigger, Wix looks up the ID in the contact ID field. If the ID corresponds to an existing contact, Wix retrieves the contact’s data and attaches that data to the payload. The data can then be passed to any subsequent actions. > **Note:** If the ID corresponds to a new contact, you can save the contact data by executing the **Create a Contact** action after the trigger. ## Error handling Here are some common errors you might encounter and their causes: | | | | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Error Message** | **Cause** | | Automation not found | No automation is found for the trigger ID passed to `runTrigger()`. This may be because the automation was deleted. | | This automation can only be executed within its designated application | This only applies to preinstalled automations. An app other than the one that created the automation tries to run it. | ## See also - [Add a Custom Trigger](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/add-a-custom-trigger.md) - [Custom Trigger SDK](https://dev.wix.com/docs/sdk/backend-modules/automations/triggers/custom-trigger/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add a Custom Trigger ## Article: Add a Custom Trigger ## Article Link: https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/add-a-custom-trigger.md ## Article Content: # Add a Custom Trigger [Custom triggers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-triggers.md) give you the ability to run an automation from anywhere in your site code. This article explains how to create a custom trigger and use the [Custom Trigger API](https://dev.wix.com/docs/sdk/backend-modules/automations/triggers/custom-trigger/introduction.md) to run your automation. ## Step 1 | Install the Automations SDK package **If you're working in the editor or Wix IDE:** 1. **Wix Studio:** In the code panel on the left of the site editor, go to **Code** ![](https://lh7-rt.googleusercontent.com/docsz/AD_4nXdfXqgbsvw2g99hQiSntBzAxxAORy6WRHLsf22RvMTIBfk1WBJpVGsF07YNTJQHDF9-15t_41vpyMexYPtsznlrxCeh3QlHt5-UWKIVXOBHAX2MLH7hiCaLj_u2qwNYg3cEHtj9tg?key=TRmYtOkLPB3ZtFBvYpkeO1-L) > **Packages & Apps**. **Wix Editor**: Make sure **Dev Mode** is [enabled](https://dev.wix.com/docs/velo/articles/getting-started/getting-oriented.md#enabling-velo). Then, in the code sidebar on the left, go to **Packages & Apps** ![](https://lh7-rt.googleusercontent.com/docsz/AD_4nXdqeMv-MqXNE-PI2_HaUzKj51PV2-k6QjjLxa5j1FbLoSO7QHpbiPee8EGecy4q0KLvUoDm5UUXvHqlyBBuyLLai7D5TalnH951XahLmXlpaoQK48QWMi3myS7yw01StZW0nj1Bag?key=TRmYtOkLPB3ZtFBvYpkeO1-L). 1. Under **npm**, click **+ Install packages from npm**. 1. Search for and install the `@wix/automations` package. ![Install the wix/automations package](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1b8b13984ebc10f4447d657269922f3a.png) 1. Repeat these steps to install the `@wix/essentials` package. **If you're working in your local IDE:** 1. Install the necessary packages on your command line: ```bash npm install @wix/automations npm install @wix/essentials ``` Now that you’ve installed the `@wix/automations` package, you’re able to use the Custom Trigger API in your site code. Before you write any code, however, you need to create an automation that uses the **Velo code trigger**. ## Step 2 | Create an automation with the Velo code trigger To create a new automation: 1. Go to the [Automations](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site\&title=Select%20a%20Site\&autoSelectOnSingleSite=true\&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Ftriggers) page in your site dashboard. 1. Click **+ New Automation** > **+ Start from Scratch** to open the builder. Give your new automation a title. 1. In the left menu, from the list of available triggers, select **Velo code trigger**. Once you select the trigger, the left panel displays instructions on how to work with the trigger, as well as the trigger ID and the payload. The bottom part of the panel also displays sample code that you can copy. ### Define the payload structure (optional) You have the option to define the trigger payload schema by inputting sample data. Wix provides you with default sample data, which you can choose to keep or replace with your own data. To define a payload schema for the trigger: 1. In the **Velo code trigger** panel, click **{..} Set Payload Data Structure**. ![Set the payload data structure](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e90961c6dcabe759d298c58a059c39e6.png) 1. Edit the displayed JSON data as necessary, then click **Preview Structure >**. A preview of your payload schema opens. ![Payload structure preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ae58f83cfc42debf8fefe5386d0cb88d.png) 1. Optionally, click **Mark Contact ID** to mark one of the UUIDs as a [contact ID](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-triggers.md#contact-data-in-the-payload-schema). ![Mark a UUID field as a contact ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/472c6319a914b597ceb0bf6e91f3296f.png) Only fields in proper UUID format appear on the list. You may only select one field to be a contact ID. Click **Save** to exit the **Mark Contact ID** modal. 1. When you’re satisfied with your payload schema structure, click **Save Structure**. You can return to edit the structure in the builder at any time. ### Copy code and complete automation setup Whether you define a payload or not, Wix provides you with the trigger ID, which you need to run your automation, as well as some example code. 1. Copy the trigger ID to use for later in your site code. At the bottom of the trigger configuration panel, click **Show code >**. ![Show the sample code to use Run Trigger](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c524894dfc1c8cff746dcc7f0c09b7b4.png) The trigger setup provides you with some sample code. Copy this code for use as a starting point in your own code. 1. Click **Apply** to save your trigger setup. 1. Select and configure at least one action as part of your automation, then activate the automation. Now that you’ve configured your automation, you can write the code to run it for any event. ## Step 3 | Implement Run Trigger in site code In order to implement Run Trigger, you need to define a [web method](https://dev.wix.com/docs/velo/apis/wix-web-module/introduction.md) in the backend that calls the method. Then you can export that web method to other code files for use. 1. If you’re working in the editor, create a new [web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) in the **Backend & Public** > **Backend** section of the code panel. If you’re working in the Wix IDE or locally, add the file under `src/backend`. 1. In the web module, paste the code you copied from the automation setup, so that your file looks like this: ```javascript import { customTrigger} from "@wix/automations"; import { auth } from '@wix/essentials'; import { Permissions, webMethod } from "wix-web-module"; export const runTrigger = webMethod(   Permissions.Anyone,   async (payload) => {     const triggerMethod = auth.elevate(automations.runTrigger);     // Your code here     await triggerMethod({       triggerId: 'triggerId',       payload,     });   } ); ``` Let’s break down the important lines in this code: **Lines 1-3**. Import necessary packages, including the `@wix/automations` package that contains the Run Trigger functionality, and the `@wix/essentials` package that provides auth functionality. **Line 5**. Create the web method to export to the frontend. **Line 8**. [Elevate](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md#elevate) Run Trigger so it runs with the correct permissions. **Lines 11-14**. Call the elevated method. In place of `triggerId`, enter the trigger ID you copied from your automation. If you defined a payload schema for your trigger, pass the `payload` argument as well. 1. Export your web method to another file, such as the frontend code for a site page. Add this line to the top of the file you want to import the method into: ```javascript import { runTrigger } from 'backend/triggers.web'; ``` 1. Call the method in the file code where necessary. To give a simple example, let’s say you add a [button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) element to your site page, and you want to run the automation every time a visitor clicks that button. You can then simply call Run Trigger in that button’s `onClick()` handler: ```javascript $w('#myButton').onClick(async () => {     runTrigger(); }) ``` If you defined a payload schema for your trigger, you can pass a payload object to Run Trigger. All payload fields are optional, but any fields you include in the payload should match the keys and types defined in the schema. 1. To test your code, either publish your site or [create a test site](https://support.wix.com/en/article/test-sites-creating-a-test-site). Your backend code may not work in Preview mode. ## See also - [About Custom Triggers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/automations/about-custom-triggers.md) - [Custom Trigger SDK](https://dev.wix.com/docs/sdk/backend-modules/automations/triggers/custom-trigger/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using reCAPTCHA to Protect Data Submission ## Article: Using reCAPTCHA to Protect Data Submission ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-re-captcha-to-protect-data-submission.md ## Article Content: # Velo Tutorial: Using reCAPTCHA to Protect Data Submission The [reCAPTCHA element](https://dev.wix.com/docs/velo/api-reference/$w/captcha/introduction.md) helps verify that site visitors are human before allowing actions like submitting data, logging in, or accessing private content. By adding it to your site, you can protect it from spam and automated abuse. This tutorial demonstrates how to add CAPTCHA to secure custom form submissions and database entries by performing the following steps: 1. Create a custom mailing list form where visitors provide their name and email. 2. Use the reCAPTCHA element to require visitors to complete a CAPTCHA challenge before submitting their information. 3. When the challenge is successfully completed and the CAPTCHA is first verified on the frontend, authorize the CAPTCHA token in the backend to ensure the form was completed by a human. 4. Insert the validated data into a database collection if the token is valid. If the token fails validation or expires, reset the CAPTCHA and display an error message. > **Notes:** > * You can add CAPTCHA to [Wix Forms](https://support.wix.com/en/article/adding-captcha-to-your-wix-forms) and [Wix Signup & Login](https://support.wix.com/en/article/site-members-editing-the-signup-settings-for-the-default-and-custom-forms) forms without code. > * To use CAPTCHA for data submission via a dataset, you must implement the submission using code. For example, use Wix Data APIs like [insert()](https://dev.wix.com/docs/velo/api-reference/wix-data/insert.md) or [save()](https://dev.wix.com/docs/velo/api-reference/wix-data/save.md) to handle the data operation and integrate CAPTCHA for protection. > * In addition to adding CAPTCHA for security purposes, you may want to [validate visitor inputs](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/about-validating-user-input-with-code.md) to make sure the data is in the correct format. ## Step 1 | Add elements to the page Add the following elements to the **Home** page: * Input elements for visitors to provide their name and email. * reCAPTCHA element to protect the data submission. * A button to submit the data upon successful CAPTCHA verification. * Text for displaying success and error messages.
## Step 2 | Create a database collection Create a [database collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) called **MailingList** to store the visitor's name and email after the CAPTCHA verification is successful. Add 2 text fields, `name` and `email`. ## Step 3 | Write the backend code While frontend CAPTCHA verification ensures the site visitor successfully completes the challenge, backend validation prevents site visitors from spoofing or bypassing CAPTCHA. To ensure complete protection, you must implement [backend authorization](https://dev.wix.com/docs/velo/api-reference/wix-captcha-backend/authorize.md) to validate the CAPTCHA token securely: 1. Add a [web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) named **submitHandler.web.js** to your backend code. 2. Use the following code to validate the CAPTCHA token and insert the data into the **MailingList** collection if the token is valid: ```javascript import { Permissions, webMethod } from 'wix-web-module'; import wixCaptcha from 'wix-captcha-backend'; import wixData from 'wix-data'; export const processSubmission = webMethod(Permissions.Anyone, (submitRequestData) => { return wixCaptcha.authorize(submitRequestData.token) .then(() => { return wixData.insert("MailingList", submitRequestData.data); }); }); ``` **Lines 1-3**: Import the `Permissions` enum and `webMethod` function from `wix-web-module`. Then import the modules you need to work with CAPTCHA in the backend and with data. **Lines 5-6**: Create a web method with permissions for anyone to call it from the frontend. **Lines 7-8**: Pass the web method a function that checks the token's authorization. **Line 9**: Insert the data into the **MailingList** collection if the token is authorized. ## Step 4 | Write the page code In your **Home** page, write the code for verifying the CAPTCHA in the frontend: 1. Import the backend function from your web module: ```javascript import { processSubmission } from 'backend/submitHandler.web'; ``` 2. Set initial element states: ```javascript $w.onReady(function () { $w("#messageText").hide(); $w("#submitButton").disable(); }); ``` **Lines 1-4**: To make sure site visitors complete a CAPTCHA challenge before being able to submit their entries, set the initial element states in the `onReady` function. Hide the text for displaying success and error messages upon submission, and disable the submit button. 2. Add the following code to enable the submit button when CAPTCHA verification is successful: ```javascript $w("#captcha").onVerified(() => { $w("#messageText").hide(); $w("#submitButton").enable(); }); ``` **Line 1**: Add an `onVerified` event handler that runs when the CAPTCHA challenge is successfully completed and the CAPTCHA is verified. **Line 2**: Hide any previous success or error messages. **Line 3**: Enable the previously disabled submit button so the site visitor can complete the submission. > **Note** > When the CAPTCHA is verified, a CAPTCHA token is automatically generated. This token is used for authorization. 3. Write code to handle data submission and reset the CAPTCHA element if the process fails: ```javascript $w("#submitButton").onClick(() => { let submitRequestData = { "token": $w("#captcha").token, "data": { "name": $w("#nameInput").value, "email": $w("#emailInput").value } }; processSubmission(submitRequestData) .then( () => { resetCaptcha("Data successfully submitted"); }) .catch( () => { resetCaptcha("Something went wrong. Redo the captcha challenge."); }); }); function resetCaptcha(messageText) { $w("#captcha").reset(); $w("#submitButton").disable(); $w("#messageText").text = messageText; $w("#messageText").show(); } ``` **Line 1**: Add an event handler that runs when a site visitor clicks the submit button. **Lines 2-7**: Prepare the submit request data. The request data includes the CAPTCHA token generated when the captcha was verified, and the data entered in the input elements. **Line 10**: Call the `processSubmission` backend function with the prepared submit request data. The `processSubmission` function validates the token and inserts the data into the **MailingList** collection. **Lines 12-24**: If authorization and data insertion are successful, display a success message and restart the CAPTCHA lifecycle for future submissions by resetting the reCAPTCHA element and disabling the submit button. If authorization or data insertion fail in the backend, restart the CAPTCHA lifecycle by resetting the reCAPTCHA element, disable the submit button, and display an error message instructing the site visitor to try again. 4. Show an error message if the CAPTCHA element loses connection with the provider: ```javascript $w("#captcha").onError(() => { $w("#messageText").text = "The reCAPTCHA element lost connection with the CAPTCHA provider. Try again later."; $w("#messageText").show() .then(() => { $w("#messageText").hide("fade", {"delay": 10000}); }); }); ``` **Lines 2-3**: If the reCAPTCHA element loses connection with the provider when the site visitor attempts to complete the CAPTCHA challenge, the reCAPTCHA element automatically resets. Set the message text to an error message asking the site visitor to try again later. **Lines 4-5**: Show the message. **Lines 6-8**: Hide the message after 10 seconds. 5. Disable the submit button if the CAPTCHA token expires: ```javascript $w("#captcha").onTimeout(() => { $w("#submitButton").disable(); }); ``` **Lines 1-3**: If the submit does not occur within 120 seconds of completing the CAPTCHA challenge, the generated token expires. When the token expires, the reCAPTCHA element automatically resets and displays a message asking the site visitor to redo the challenge. In this case, disable the submit button in the `onTimeout` event handler. ### Example code Here is the complete code for this example: #### Page code ```javascript import { processSubmission } from 'backend/submitHandler.web'; $w.onReady(function () { $w("#messageText").hide(); $w("#submitButton").disable(); }); $w("#captcha").onVerified(() => { $w("#messageText").hide(); $w("#submitButton").enable(); }); $w("#submitButton").onClick(() => { let submitRequestData = { "token": $w("#captcha").token, "data": { "name": $w("#nameInput").value, "email": $w("#emailInput").value } }; processSubmission(submitRequestData) .then( () => { resetCaptcha("Data successfully submitted"); }) .catch( () => { resetCaptcha("Something went wrong. Redo the captcha challenge."); }); }); $w("#captcha").onError(() => { $w("#messageText").text = "The reCAPTCHA element lost connection with the CAPTCHA provider. Try again later."; $w("#messageText").show() .then(() => { $w("#messageText").hide("fade", {"delay": 10000}); }); }); $w("#captcha").onTimeout(() => { $w("#submitButton").disable(); }); function resetCaptcha(messageText) { $w("#captcha").reset(); $w("#submitButton").disable(); $w("#messageText").text = messageText; $w("#messageText").show(); } ``` #### Backend code ```javascript import { Permissions, webMethod } from 'wix-web-module'; import wixCaptcha from 'wix-captcha-backend'; import wixData from 'wix-data'; export const processSubmission = webMethod(Permissions.Anyone, (submitRequestData) => { return wixCaptcha.authorize(submitRequestData.token) .then(() => { return wixData.insert("MailingList", submitRequestData.data); }); }); ``` ### See also * [$w.captcha API](https://dev.wix.com/docs/velo/api-reference/$w/captcha/introduction.md) * [wix-captcha-backend API](https://dev.wix.com/docs/velo/api-reference/wix-captcha-backend/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Using the Fetch API to Add a Currency Converter ## Article: Getting Started with Velo by Wix ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/tutorial-using-the-fetch-api-to-add-a-currency-converter.md ## Article Content: # Tutorial: Using the Fetch API to Add a Currency Converter In this tutorial, you'll learn how to create a simple currency converter site that uses the [wix-fetch API](https://www.wix.com/velo/reference/wix-fetch.html) to connect to a 3rd-party service. Site visitors choose source and target currencies from dropdowns and enter an amount to convert. The results are displayed in a text box.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/03/07/37a87ea6-7a20-4902-b51a-0fe6acecd54a.png)
Follow the steps below to create a currency converter with the wix-fetch API. ### Step 1: Create a New Wix Site 1. Sign into your Wix account or [sign up for a Wix account](https://users.wix.com/signin?overrideLocale=en&loginDialogContext=signup) if you don’t already have one. 2. Open a blank template in [Wix Studio](https://wixstudio.new/) or the [Wix Editor](https://editor.wix.com/html/editor/web/renderer/new?siteId=cbf36d3a-49d0-41c2-9482-1bb58d5fdda3&metaSiteId=a573279f-ae6f-46d1-8556-7c93ae9b2c84&editorSessionId=799795e9-07c2-4e7e-8a17-33dab829f776). ### Step 2: Enable Velo Dev Mode + **Wix Studio:** In the Code panel, click **Start Coding**. ![Studio start coding](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d7e450ac2ad3fe809d63c73c620c9fbc.png) + **Wix Editor:** In the top bar of the Wix Editor, click **Dev Mode > Turn on Dev Mode**. ![Velo dev mode](https://wixmp-833713b177cebf373f611808.wixmp.com/images/429b079021e6918721d24e16cd100720.png) ### Step 3: Add Elements to the Page Add page elements in the editor: 1. On the left side of the editor, click **Add Elements**. 2. Add the page elements illustrated below to your site. * When you add each element, set its ID in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). In Wix Studio it appears at the bottom of the code editor, and in Wix Editor, the Properties & Events panel appears on the right side of the code editor. Use the name shown below for each element, minus the hashtag. See the table below for a full list of the elements and where to find them in the Add menu.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/10/25/0cb6d2f8-071b-4f56-90a6-f307dcae7c5f/11c3401f-811c-4a02-8c62-e89ccb83a983.png)
|Element |Location in Add Menu |Description |ID | |---|---|---|---| |Dropdown|User Input|For selecting the source currency|sourceCurrency |Dropdown|User Input|For selecting the target currency|targetCurrency |Input|User Input|For entering the amount to convert|sourceAmount |Text Box|User Input|To display the converted amount|targetAmount |Button|Button|To trigger the currency conversion when clicked|calculateButton ### Step 4: Add Code >**Notes** > * All the code for this example is added to a single page on the site. In this section we divided the code into short blocks followed by explanations. To see the complete code for this example without comments, [scroll down](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/tutorial-using-the-fetch-api-to-add-a-currency-converter.md) to the end of the tutorial.  > * See our [API Reference](https://www.wix.com/velo/reference/) to learn more about the Velo-based code in this example. **To add the code:** 1. + **Wix Studio:** On the left, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) **> Page Code**. + **Wix Editor:** In the Code sidebar, click **Page Code**. Under **Main Pages** double-click the page labeled **HOME**. 2. Add the following code to the top of the code in the tab before the [onReady](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) function: ```javascript // The getJSON function in wix-fetch lets you retrieve a // JSON resource from the network using HTTPS. import {getJSON} from 'wix-fetch'; // Set the URL of the 3rd-party service. const url = "https://api.exchangerate.host/convert"; // Define the currency option values and text for the dropdowns. let currencyOptions = [ { "value": "USD", "label": "US Dollars"}, { "value": "EUR", "label": "Euros"}, { "value": "JPY", "label": "Japanese Yen"}, ]; ``` 3. Add the code below to the [onReady](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) function. Code inside the onReady function runs when the page loads. ```js $w.onReady(function () { // Set the currency options for the dropdowns. populateDropdowns(); // Set the onClick event handler for calculateButton to calculate the target amount. $w('#calculateButton').onClick((event) => { calculateCurrency(); }) }); ``` The [`$w`](https://www.wix.com/code/reference/$w.html#$w) function can select elements on a page by ID or by type, allowing us to run functions and define the properties of the elements. Use this syntax to select an element by ID, `$w("#myElementId")`, and this syntax to select by type, `$w("ElementType")`. Here we select the button and define an `onClick` [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) to calculate the target amount. 4. Add code to define the functions: **populateDropdowns( )** ```js // Populate the dropdowns. function populateDropdowns(){ //Set the dropdown options. $w("Dropdown").options = currencyOptions; // Set the first dropdown option as the initial option. $w("Dropdown").selectedIndex = 0; } ``` Here we select all the dropdowns by type. By calling [`$w`](https://www.wix.com/velo/reference/$w.html) with the element type "Dropdown", we select all dropdowns on the page **calculateCurrency( )** ```js // Calculate the target amount. function calculateCurrency() { // Initial amount let initialAmount = $w("#sourceAmount").value; // Original currency let sourceSymbol = $w("#sourceCurrency").value; // Target currency let targetSymbol = $w("#targetCurrency").value; // Define the full url. let fullUrl = `${url}?from=${sourceSymbol}&to=${targetSymbol}`; // Call the wix-fetch API function to retrieve the JSON resource. getJSON(fullUrl) .then(json => { // Set the target amount as the initial amount multiplied by // the conversion rate. $w("#targetAmount").value = initialAmount * json.info.rate; } )} ``` We use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to define the full URL, which includes the source and target currencies. The wix-fetch API [`getJSON`](https://www.wix.com/velo/reference/wix-fetch.html#getJSON) function retrieves the JSON resource using the full URL. `getJSON` returns a [promise](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md), which resolves to a JSON object. We multiply the retrieved rate by the initial amount and assign it to the `targetAmount` text box. ### Step 5: See It in Action Now it's time to test your site: 1. In the top right corner of the editor, click ![Preview icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/e65d33fd-497b-430a-8719-ff4d10488773) (Wix Studio) or **Preview** (Wix Editor). 2. Enter an amount in the source currency input. 3. Click the calculate button and see the converted currency result in the target amount text box. 4. [Publish](https://support.wix.com/en/article/publishing-your-site-6980885) your site to make it live and production ready. That's it! In just a few minutes, you created a web application in Velo! No setup, no managing server infrastructure, just integrating Velo APIs with the Wix visual builder. ### Next Steps Now that you've had a taste of Velo, check out what else you can do: * Easily call backend code from the frontend using [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md).  * Work with Wix's [visual builder](https://support.wix.com/en/article/getting-started-with-the-wix-editor). * Add features and customize your site using [Velo APIs](https://www.wix.com/velo/reference/api-overview/introduction). Here are some examples of what you can do: * [Collect](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-a-single-product.md) payments * [Create](https://support.wix.com/en/article/velo-tutorial-expand-text-with-a-read-more-link) a show-more link * [Use](https://www.wix.com/velo/example/create-a-custom-chart) an HTML component to embed a chart on your page * Work with the data from [other Wix Apps](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) like [Stores](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-collections-collection-fields.md), [Bookings](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-services-collection-fields.md), and [Events](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-events/wix-events-events-collection-fields.md) * Visit the [Velo by Wix website](https://www.wix.com/velo) to onboard and continue learning. Check out Velo [documentation](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) and the [API Reference](https://www.wix.com/velo/reference). ### Example Code Here is the complete code for this example, without comments: ```javascript import {getJSON} from 'wix-fetch'; const url = "https://api.exchangerate.host/convert"; let currencyOptions = [ { "value": "USD", "label": "US Dollars"}, { "value": "EUR", "label": "Euros"}, { "value": "JPY", "label": "Japanese Yen"}, ]; $w.onReady(function () { populateDropdowns(); $w('#calculateButton').onClick((event) => { calculateCurrency(); }) }); function populateDropdowns(){ $w('Dropdown').options = currencyOptions; $w('Dropdown').selectedIndex = 0; } function calculateCurrency() { let initialAmount = $w("#sourceAmount").value; let sourceSymbol = $w("#sourceCurrency").value; let targetSymbol = $w("#targetCurrency").value; let fullUrl = `${url}?from=${sourceSymbol}&to=${targetSymbol}`; getJSON(fullUrl) .then(json => { $w("#targetAmount").value = initialAmount * json.info.rate; } )} ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Use External API Keys Stored in the Secrets Manager to Call the OpenWeatherMap API ## Article: Use External API Keys Stored in the Secrets Manager to Call the OpenWeatherMap API ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/use-external-api-keys-stored-in-the-secrets-manager-to-call-the-open-weather-map-api.md ## Article Content: # Tutorial: Use External API Keys Stored in the Secrets Manager to Call the OpenWeatherMap API In this tutorial, you'll learn how to securely use external API keys stored in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) to call the [OpenWeatherMap API](https://openweathermap.org/api). By keeping your API keys safe and confidential with the Secrets Manager, you can make secure API requests without exposing sensitive information in your code. We'll use the following steps to call the OpenWeatherMap API with external API keys: 1. Save your API key in the Secrets Manager. 2. Configure your code to retrieve the API key and call OpenWeatherMap. ## Step 1 | Save your API key in the Secrets Manager 1. Access the Secrets Manager. There are 2 ways to navigate to the Secrets Manager: - Select **Developer Tools** from the code panel. Under the Security section, select **Secrets Manager**. - Select **Developer Tools** in your site's dashboard, and then select **Secrets Manager**. 2. Add and save the API key for the OpenWeatherMap API. For purposes of this tutorial we have named this key "WeatherApiKey". ## Step 2 | Write code to retrieve the API key and call OpenWeatherMap Create a function in a web method to call the weather service and retrieve the data: 1. Import the functions needed to make `https` requests and to get secrets from the Secrets Manager. For example: ```javascript import {Permissions, webMethod} from "wix-web-module"; import {fetch} from 'wix-fetch'; import {getSecret} from 'wix-secrets-backend'; ``` 2. Create a new function and do the following: a. Takes in a `city` whose weather you want to look up. b. Defines the service's address. c. Retrieves the API key from the Secrets Manager. For example: ```javascript export const getCurrentTemp = webMethod(Permissions.Anyone, async (city) => { const url = 'https://api.openweathermap.org/data/2.5/weather?q='; const key = await getSecret(WeatherApiKey); // ... } ``` 3. Continue the function by constructing the full URL for the fetch request. The URL is made up of the service's address and an API key. For example: ```javascript let fullUrl = url + city + '&APPID=' + key + '&units=imperial'; ``` 4. Finish the function by receiving a response from the API call. When you receive the response, pull out the temperature data and return it. For example: ```javascript return fetch(fullUrl, {method: 'get'}) .then(response => response.json()) .then(json => json.main.temp); ``` ### Example Code Here is the complete code for this example, without comments: ```javascript // serviceModule.web.js import {Permissions, webMethod} from "wix-web-module"; import {fetch} from 'wix-fetch'; import {getSecret} from 'wix-secrets-backend'; export const getCurrentTemp = webMethod(Permissions.Anyone, async (city) => { const url = 'https://api.openweathermap.org/data/2.5/weather?q='; const key = await getSecret(WeatherApiKey); let fullUrl = url + city + '&APPID=' + key + '&units=imperial'; return fetch(fullUrl, {method: 'get'}) .then(response => response.json()) .then(json => json.main.temp); }); ``` ## See also - [Wix Fetch API](https://dev.wix.com/docs/velo/api-reference/wix-fetch/introduction.md) - [Wix Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding Custom Interactivity with Events ## Article: Adding Custom Interactivity with Events ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/adding-custom-interactivity-with-events.md ## Article Content: # Velo Tutorial: Adding Custom Interactivity with Events In this tutorial, we’ll show you how to set up an image so that it’s hidden when the page loads, only becoming visible when your visitor clicks a button. We’ll start by adding an image and button to a page, then set up the button to run some code when it’s clicked. Then we’ll configure an image so that it doesn’t automatically load with the page. Finally, we’ll add the code that will make the image appear, with an animation, when the button is clicked. ## 1\. Enable Velo - **Wix Editor:** Click **Dev Mode** in your site's top bar and turn on **Enable Developer Mode** in the dropdown. - **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**.. ## 2\. Set Up an Element to React to a User Action Your site can react to user actions with [event handlers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). When you add an event handler to an element, you’re telling your site to watch the element to see if that event occurs. If it does, you’ll want your site to run some code.  **To set up an image to display when a button is clicked:** 1. Add a **button** and an **image** to your page. 2. Select the **button** and open the **Properties & Events panel** and then add the **onClick** event handler. 3. The function for your event handler is added to your code in the code editor: ```js $w('#button1').onClick((event) => { }) ``` ## 3\. Add Code to Your Event Handler Now you’ll need to set up the image so it’s not visible when the page loads. Then add code so that the image appears when the button is clicked. Interactions are coded using the Velo APIs. For example, to make an image appear, you’ll use the [`.show()`](https://www.wix.com/velo/reference/$w/image/show) function for the image element. **To set an image to be hidden on load and to show on a button click:** 1. Select the **image** and open the **Properties & Events panel**, then select **Hidden**. 2. In the code editor, add the `.show()` function to your image element using the **image ID** which can be found in the **Properties & Events panel**. ```js $w('#button1').onClick((event) => { $w('#image1').show(); }) ``` ## 4\. Add an Animation Many functions accept an optional parameter to change how the function works. For example, you can add animations like FadeIn, Drop-In, Fly-In, and Spin-In to the `.show()` function using the `effectName` parameter. ​To add a Slide-In animation when the button is clicked, use `'slide'` as the `effectName` parameter as follows: ```js $w('#button1').onClick((event) => { $w('#image1').show('slide'); }) ``` Now when the button is clicked, the `.show()` function runs on your image with the `'slide'` animation. >**Note:** > To make your image appear without an animation, don't add any parameters to your function. ## Next Steps With Velo, you can do even more: * Add more events and event handlers using the [Velo APIs](https://www.wix.com/velo/reference/api-overview). * Make sections with alternating layouts using the [Slideshow](http://wix.to/94BuAAs/$w.Slideshow.html) element. * Add collapsible sections.  * Add interactions on elements as they enter the viewport. * Manipulate Text, Image, Gallery, Repeaters, and other elements using code. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Change the Text Label of a Button with Events ## Article: Change the Text Label of a Button with Events ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/change-the-text-label-of-a-button-with-events.md ## Article Content: # Velo Tutorial: Change the Text Label of a Button with Events An **Event** is something that happens to an element in your site. The **Events Handlers** section in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) lets your site respond to these events by adding **Event Handlers** to your elements. For example, let's say you have a site with a "Take the tour" button. You'd like the text on the button to change to "Let's Go!" when the user hovers over it. Here's what you would do: 1. Use the Add panel to add a button to your site. 2. Change its text to "Take the tour". ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/change-the-text-label-of-a-button-with-events-md_velo-articles_code-tutorials_images_button_settings_sm.jpg) 3. The Properties & Events panel shows you the default properties for the button you just added. 4. In the Properties Panel, click on the **ID** name to rename the element to **takeTourButton** and press **Enter**. This makes the element easier to identify. (This isn't required, but it is recommended.) 5. In the **Event Handlers** section click **onMouseIn,** click the **+** , and press **Enter**. The name of the onMouseIn event handler is displayed. 6. Code is automatically added to the code editor. This is what you'll see there: ```javascript $w('#takeTourButton').onMouseIn((event) => { // Add your code for this event here: }) ``` 7. Let's add the code that changes the text on the button. Afterwards we'll go back and see how it worked. Under line 7 where it says:  **//Add your code for this event here:** add a line and type **$**.  8. A popup window opens that contains a list of all the elements in your site surrounded by some code. Use the arrow keys to move down and select **$w("#takeTourButton")**. 9. Press **Enter** to add the code for the element you selected. ```javascript $w('#takeTourButton').onMouseIn((event) => { // Add your code for this event here: $w('#takeTourButton'); }) ``` 10. Now enter a period at the end of the line and you'll see a popup window that contains a list of all the properties, methods, and events that you can use with your element. Use the arrow keys to scroll down and select the **label** property. 11. Press **Enter** to add the code you selected. ```javascript $w('#takeTourButton').onMouseIn((event) => { // Add your code for this event here: $w('#takeTourButton'); }) ``` 12. To set the label to its new value add **\= "Let's Go!";** at the end of the line and save your work. ```javascript $w('#takeTourButton').onMouseIn((event) => { // Add your code for this event here: $w('#takeTourButton').label = "Let's Go!"; }) ``` And that's it! Click **Preview** and you can test that your code works. Hover over the "Take the tour" button and it will change to look like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2021/02/02/ee1c68d0-3157-4a4e-8242-71fc1fa02972/2d0b64ae-ea1f-481f-91b3-8a581ccb4d31.png)
### Understanding the Code When you selected the **onMouseIn** event handler in the **Event Handlers** section two things happened - one of them you can see and the other you can't. The thing you _can_ see is that these lines were added to the code editor for your page, which add the **takeTour\_mouseIn** function to your page code: ```javascript $w('#takeTourButton').onMouseIn((event) => { // Add your code for this event here: }) ``` This function is a block of code that will run whenever takeTourButton is hovered on. The way that works has to do with the thing that happened that you can't see. The thing you _can't_ see is that Velo adds code to your page that watches or "listens" to your button to see if it's ever hovered on. If it is, Velo knows it needs to run any code that's inside the takeTourButton mouseIn function. That means any code between the { and } is run whenever takeTourButton is hovered on. Now we come to the code that you created inside your event handler function. Some of what you needed to enter in the code editor is simply code syntax. Syntax is a set of rules that lets us communicate using code. In our case, the first thing we need to do is tell the code that we want to do something to takeTourButton. We do that with **$w("#takeTourButton")**. Why? You can obviously see the takeTourButton element name in there. The rest is the syntax that tells the code "This isn't just words, it's the name of an element on my page I want to do something to." Each element has certain properties, methods, and events associated with it. Properties are the things about your element that you might want to know or change. In the example above we used the label property to set the label text of our button. You may have noticed that there were other properties like `link` and `id`, and methods like `hide` and `isEnabled`, that were also in the list for the button. The list of properties, methods, and events associated with an element is specific to that element. For example, a Text Element doesn't have the `isEnabled` method because it doesn't make sense to enable a Text Element. But a button can be enabled (clickable) or disabled (not clickable). Then we added a period at the end of the element. This tells the code "Now I want to work with one of the properties, methods, or events that are associated with my element." `label` is the property that controls the text that appears on a button, so we selected it. Then all we needed to do was assign a new text to the label property, in our case "Let's Go!" The semicolon at the end is standard JavaScript syntax that says "this is the end of a line." >**Note:** > In this tutorial the event and the response to that event both happen on the same element. We add an event that watches to see if the user hovers on a button, and if they do we change the label of that button.  > > It's important to note that you aren't limited to the event and its response being on the same element and that any event on any action can create a response on any other element in your site. It all depends on what element you select in the code you put inside your function. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Expand Text with a Read More Link ## Article: Expand Text with a Read More Link ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/expand-text-with-a-read-more-link.md ## Article Content: # Velo Tutorial: Expand Text with a Read More Link This tutorial describes how to use [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) to let site visitors expand and collapse text with "Read More" and "Read Less" buttons. 
### Overview To create Read More / Read Less functionality, we use a [multi-state box](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/about-multi-state-boxes.md). Multi-state boxes are great for switching between several views. They contain multiple states with different content, and display one state at a time. One state in our multi-state box will contain the shorter (collapsed) content and one state will contain the longer (expanded) content. We'll use code to enable site visitors to switch between the 2 states by clicking "Read More" and "Read Less" buttons. ### Step 1: Add a Multi-State Box **To add a multi-state box to your page:** Wix Studio: 1. If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. 2. Click the **Add** panel and select **Layout Tools > Multi-state Boxes**. 3. Drag a multi-state box element onto your page. Wix Editor: 1. Make sure Velo Dev Mode is [enabled](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md). 2. Click **Add** ![](https://d2eyqiy4n03ve6.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2015/08/05/f5f1bce8-77f0-405c-8520-f147e1035fb1.png) on the left side of the editor. 3. Click **Interactive**. 4. Drag a multi-state box element onto your page.
Remove a border from a pre-designed multi-state box and blend it in with your page background Click on the pre-designed multi-state box and update the design as follows: 1. Set the border width to 0. 2. Make sure the shadow is disabled. 3. Change the box's background color to match the color of your page's background color. >**Notes:** > - Pre-designed multi-state boxes aren't currently supported in Wix Studio. > - You can't remove the border from a blank multi-state box.
### Step 2: Rename Your Multi-State Box and State When you click your multi-state box, you can see the ID (name) of the multi-state box in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). When you click **Manage States**, you can see the IDs of the default states for the multi-state box. You can rename both your multi-state box and your states in the Properties & Events panel. It's a good idea to give your IDs meaningful names, since you'll be using them in code. We renamed our multi-state box to `readMoreStatebox` and our state to `collapsedState`, since this state will have the collapsed version of the text. ### Step 3: Add Content to Your Collapsed (Short) State Now you can add your page content to your state: images, videos, text, or other elements. Make sure the elements fit within the borders of your multi-state box so they'll be attached to the state. Since this is the collapsed state, add the short version of your text. ### Step 4: Add a "Read More" Button When you're finished setting up your state, do the following: 1. Add a transparent button (with no background or border) to your state from the Add panel. 2. Change the button text to **Read More** and match the font type and size to the rest of your text. 3. Rename the button ID to `readMoreButton` in the Properties & Events panel. 4. Append the button to the end of the collapsed text. ### Step 5: Duplicate Your State Now you can duplicate your collapsed state and then adjust it to create the expanded state: 1. Click your multi-state box and click **Manage States**. 2. Click **Duplicate State**. Now you're in the second state of your multi-state box. >**Note** > You can switch between your states by clicking **Manage States** and selecting the state you want to edit. ### Step 6: Set Up Your Expanded (Long) State 1. Rename the duplicated state ID to `expandedState` in the Properties & Events panel. 2. Add the longer text to this state. You can resize the multi-state box if you need by dragging its handles at the edge of the box. 3. Change the **Read More** button text to **Read Less**. 4. Rename the button ID to `readLessButton` in the Properties & Events panel. 5. Move the button to the end of the longer text. ### Step 7: Add Code Now we need to write code to define when to switch between the collapsed state and expanded state. We use the [MultiStateBox API](https://www.wix.com/velo/reference/$w.MultiStateBox.html) to define when to switch states. 1. Open the code panel. + Learn how to work with the **Wix Studio** [code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md). + Learn how to work with the **Wix Editor** [code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). 2. Add the code in lines 2-8 below to your `onReady()` function so that the code on your page looks like this: ```javascript $w.onReady(function () { $w("#readMoreButton").onClick(() => { $w("#readMoreStatebox").changeState("expandedState"); }); $w("#readLessButton").onClick(() => { $w("#readMoreStatebox").changeState("collapsedState"); }); }); ``` #### Understanding the Code * **Line 2:** When the **Read More** button is clicked, do the following: * **Line 3:** Change the current state of the `readMoreStatebox` multi-state box to the `expandedState`. * **Line 6:** When the **Read Less** button is clicked, do the following: * **Line 7:** Change the current state of the `readMoreStatebox` multi-state box to the `collapsedState`. Preview your site to make sure everything is working as expected. Then go ahead and publish. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Hiding an Element on Certain Pages ## Article: Hiding an Element on Certain Pages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/hiding-an-element-on-certain-pages.md ## Article Content: # Velo Tutorial: Hiding an Element on Certain Pages This tutorial explains how you can use [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) to show an element on all pages except for some of them. In other words, you have an element on your site that is part of a [global section](https://support.wix.com/en/article/studio-editor-using-global-sections#setting-a-global-section) (Wix Studio), or that is set to [show on all pages](https://support.wix.com/en/article/showing-an-element-on-all-pages-6533570) (Wix Editor), and you want to hide it on one or more of your pages. Note that some elements appear on all pages by default based on their functionality, without this setting (for example, the Wix Chatbox). These elements don't require this setting. You can still use the code in this tutorial to hide them. This tutorial has 2 parts: * Instructions on how to get set up, including code you can copy and paste onto your page * An explanation of what each line of code does > > The functionality described in this tutorial works only on your published site. ### Instructions 1. Start with an element that is part of a [global section](https://support.wix.com/en/article/studio-editor-using-global-sections#setting-a-global-section) (Wix Studio), or is set [to show on all pages](https://support.wix.com/en/article/wix-editor-showing-an-element-on-all-pages) (Wix Editor). 2. Navigate to a page where you don't want that element to appear. 3. Copy the code below and paste it in your page code under the line that says "// TODO: write your JavaScript here…" ```javascript // Hides the element when the page loads $w("#myElement").hide(); ``` 1. Make sure to make this substitution: * _#myElement_: the ID of the element that you want to hide on the current page (hover over it to see its ID) 2. Publish the site and view the page to make sure the element does not appear. If you want to hide other elements, just add all their element IDs to the line of code, like this: ```javascript // Hides these elements when the page loads $w("#myElement, #myElement2, ...").hide(); ``` ### Understanding the Code The image below shows what the code looks like in the code editor. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/04/10/4be8155b-9790-41c8-94a7-6697fb9fe93a.png) Line 3 calls the [`onReady()`](https://www.wix.com/velo/reference/$w/onready) function. This defines the code that will run when the page is finished loading. Line 5 calls the `hide()` function, which sets the element's `hidden` property to `true`. This means the element will not be displayed on this page even if the element is part of a global section (Wix Studio), or is set to "Show on all pages" (Wix Editor). If you decide you want to display the element, you can change the `hide()` function to `show()`. One thing to keep in mind is this: a hidden element still takes up room on the page. In other words, depending on the layout of your page, even though the element is hidden, it could still leave a gap. Instead of hiding your element, you can use the `collapse()` function, because a collapsed element does not take up any space on the page. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Capturing and Displaying Ratings ## Article: Capturing and Displaying Ratings ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/capturing-and-displaying-ratings.md ## Article Content: # Velo Tutorial: Capture and Display Ratings This tutorial shows you how you can capture and display ratings of your items from your visitors. You will set up a Ratings Display element to display the current average rating and the total number of ratings. Then using a Ratings Input element, you'll capture your visitor's rating, calculate the new average rating and total number of ratings, and then update the collection and the Ratings Display element with the new values. 
You'll need a collection with items you want to let your visitors rate and a page with a dataset connected to that collection.  This tutorial has 3 parts: * [How to set up your collection and your page](#set-up) * [Code you can copy and paste onto your page](#the-code) * [A detailed explanation of the code](#code-explanation) ## Set up This section covers what you need to prepare in your collection and what you need to do in your page in the editor. #### In your collection 1. Make sure your collection's [permissions](https://support.wix.com/en/article/changing-your-database-collection-permissions) allow visitors to view and add content. 2. Add 3 new Number fields, for the average rating, number of ratings submitted, and sum of all the ratings submitted. You can leave these fields blank or input starting values. Remember that the average rating must be between 1 and 5. #### In your page 1. Set your dataset's [mode](https://support.wix.com/en/article/working-with-dataset-modes-and-collection-permissions) to "Read and Write." This will let your visitors update information in the collection. 2. Add the following elements: 1. A [Ratings Display](https://support.wix.com/en/article/working-with-the-connect-ratings-panel) element connected to the new Number fields in your collection. 2. A [Ratings Input](https://support.wix.com/en/article/ratings-input) element your visitors can use to pick a rating for the item. You can use the default setting for the ratings, or define your own. Don't connect it to the dataset. ## The code This section has 2 parts. The first part shows you how to add the event handler to the Ratings Input element. The second part has the actual code that you can copy and paste onto your page. #### Add the event handler An [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) adds code that only runs when your visitor performs a certain action. In our code, we used the "onChange" event, so that the rating is saved when our visitor makes their selection in the Ratings Input. **To add the event handler:** 1. Select your Ratings Input. 2. In the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md), click the "onChange" event handler. The code for the event handler is added to the bottom of your page code. The image below shows what it looked like on our page. ```js $w('#ratingsInput1').onChange((event) => { //Add your code for this event here: }) ``` #### Add the code 1. Copy the code below and paste it into the event handler method above the line that says "//Add your code for this event here:". You can delete that line if you want. ```javascript $w("#dataset1").onReady(() => { // get the current item from the dataset const currentItem = $w("#dataset1").getCurrentItem(); // get the current average rating, number of ratings, and //total ratings for the current dataset item const average = currentItem.avg; const count = currentItem.numRatings; const total = currentItem.totalRatings; // get the new rating from the ratings input const newRating = $w('#ratingsInput1').value; // calculate the new average rating based on the current //average and count const newAverageLong = (total + newRating) / (count +1); // Round the average rating to 1 decimal point const newAverageShort = Number.parseFloat(newAverageLong).toFixed(1); // set the dataset fields to the new average, total // ratings, and number of ratings $w('#dataset1').setFieldValues({ 'avg': newAverageShort, 'totalRatings': total + newRating, 'numRatings': (count + 1) }); // save the dataset fields to the collection $w('#dataset1').save() .catch((err) => { console.log('could not save new rating'); }); }); ``` 2. Make sure to make these substitutions. * `'#myDataset1'`: The ID of your dataset * `avg`: The field ID of the field in your collection that holds the average rating * `numRatings`: The field ID of the field in your collection that holds the total number of ratings received * `totalRatings`: The field ID of the field in your collection that holds the sum of all ratings received * `'#ratingsInput1'`: The ID of your Dropdown element 3. Preview your page and test out the Ratings Input element. Watch how the ratings display element updates when you add a rating.  4. Go back to the Content Management System (CMS) to see how the average rating and the total number of ratings for that item have been updated in your collection. ## Code explanation First we read the current item from the dataset. Then we define variables called `average`, `count` and `total`, and set their values to be the item's current average rating, number of ratings, and the sum of all the ratings. ```js const currentItem = $w("#myDataset").getCurrentItem(); const average = currentItem.averageRating; const count = currentItem.numRatings; ``` We define a variable called `newRating` and set its value to be the rating selected by the user in the Ratings Input. `const newRating = Number($w('#ratingsInput1').value);` Next we calculate the updated average rating and save it to a variable called `newAverageLong`. We then round the results to 1 decimal place and assign it to the variable `newAverageShort`. ```js const newAverageLong = (total + newRating) / (count + 1); const newAverageShort = Number.parseFloat(newAverageLong).toFixed(1); ``` Now we save the new average rating, sum of all the ratings, and total number of ratings to the current item. We include code to deal with errors should they occur. ```js $w('#dataset1').setFieldValues({ 'avg': newAverageShort, 'totalRatings': total + newRating, 'numRatings': (count + 1) }); $w('#dataset1').save() .catch((err) => { console.log('could not save new rating'); }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating a Rich Text Editor Using the HTML Component ## Article: Creating a Rich Text Editor Using the HTML Component ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/creating-a-rich-text-editor-using-the-html-component.md ## Article Content: # Velo Tutorial: Creating a Rich Text Editor Using the HTML Component Before reading this article, you might want to learn about [Working with the HTML Component in Velo](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md). In this article, we demonstrate how to use an HTML Component to add a rich text editor to your page.  We start by creating the following page:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/08/29/4fbb8d5d-33e3-40a8-9bb4-c83eba9855b7.png)
The left side of the page is a Wix Text element. The right side of the page is created with an HTML Component. The component contains HTML that creates a [CKEditor](https://ckeditor.com/) and a button.  Using this setup, a user can enter rich text in the rich text editor, click the **Submit text** button, and the rich text will be displayed in the Wix text element. #### Page Code The following code is added to the code editor. In the page's `onReady()` event handler, we set an event handler that runs when the page receives a message from the HTML Component. The event handler sets the text of the page's Text element to be the data sent from the HTML Component. ```javascript $w.onReady(function () { $w("#myHtmlComponent").onMessage( (event) => { $w('#myText').html = event.data; } ); } ); ```
**Note:** We are using the Text element's **html** property to set its styled text. To learn about how a Text element displays content that is set using the **html** property, see [Formatting Text Elements with Velo](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/about-formatting-text-elements.md).
#### HTML Component Code The following code is added to the HTML Component using the **Enter Code** or  **Edit Code** button. It contains three main sections. In the `` tag, we define the HTML elements that make up our page: * A `
``` ### API List The following API is used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). * [HtmlComponent](https://www.wix.com/velo/reference/$w.HtmlComponent.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Redirecting Visitors Based on Browser Language ## Article: Redirecting Visitors Based on Browser Language ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/redirecting-visitors-based-on-browser-language.md ## Article Content: # Velo Tutorial: Redirecting Visitors Based on Browser Language You can use Velo to redirect visitors to different language versions of your site. Most browsers have settings that let you define what language to view content in. Velo can access these settings and use them when displaying your site. For example, if your browser locale is set to an English-speaking country, you can use Velo to redirect visitors to your site in English. If your browser locale is set to a Spanish-speaking country, you can redirect visitors to the Spanish version of your site. This tutorial has 2 parts: * Instructions on how to get set up, including code you can copy and paste onto your page * An explanation of what each line of code does ### Instructions 1. Go to the page to redirect the visitor from. 2. Open the code editor. 3. Copy and paste the code below. 4. In the copied code, make sure to substitute your locale codes and URLs, removing the `<` and `\>` symbols. **Example** For French, substitute `` with `fr`. To change the URL and its locale, substitute `http:///?lang=` with `http://www.theCatalog.com/?lang=fr`. **Tip:** To get the URL, just go to your live site, navigate to the relevant page in the right language and copy it the URL. 5. Publish the page to see the redirect in action. The redirect will not work in preview mode. ```javascript import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { setTimeout(function () { const url = wixLocationFrontend.url; const shortLocale = wixWindowFrontend.browserLocale.substring(0,2); switch (shortLocale) { case '': if (url !== 'http:///?lang=') { wixLocationFrontend.to('http:///?lang='); } break; case '': if (url !== 'http:///?lang=') { wixLocationFrontend.to('http:///?lang='); } break; ... ... ... default: if (url !== 'http:///?lang=' && url !== 'http://') { wixLocationFrontend.to('http:///?lang='); } break; } }, 6500); }); ``` A sample implementation of this code is provided below. ### Understanding the Code We are now going to understand the code using a scenario. The sample code below checks the browser locale, and then redirects the visitor to a catalog (www.theCatalog.com) in the right language. The code checks if the language of the browser is French, German, Chinese, or Spanish. If none of these match, the catalog displays in English. Import statements on lines 2 and 3 bring in the APIs we need: * [`wixWindowFrontend.browserLocale`](https://www.wix.com/velo/reference/wix-window.html#browserLocale) checks the locale setting of the browser. * [`wixLocationFrontend.to`](https://www.wix.com/velo/reference/wix-location.html#to) redirects the visitor to the specified URL. ```javascript // Import statements for the Wix Location Frontend and Window APIs. import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; ``` The redirect occurs after a time delay. This gives the visitor time to see notices on the page you are redirecting from. Line 4 contains the setTimeout function that creates this delay. ```javascript $w.onReady(function () { // Adds a time delay so the visitor anticipates the redirect. setTimeout(function () { ... ... ... // The timeout value is 6500 nanoseconds. }, 6500); }); ``` We add a **switch** statement on line 16 to check the browser locale.  ```javascript import wixLocationFrontend from 'wix-location-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { // Adds a time delay so the visitor anticipates the redirect. setTimeout(function () { // Not sure which format your browser uses when // returning the locale (for example, "en" or "en-EN)? // Shorten the locale to its first two characters. const url = wixLocationFrontend.url; const shortLocale = wixWindowFrontend.browserLocale.substring(0,2); // Checks the browser locale setting. switch (shortLocale) { // Depending on the locale, redirects to a new page. // If already on the correct page, no need to // re-navigate and reload the page. case 'fr': if (url !== 'http://www.theCatalog.com/?lang=fr') { wixLocationFrontend.to('http://www.theCatalog.com/?lang=fr'); } break; case 'de': if (url !== 'http://www.theCatalog.com/?lang=de') { wixLocationFrontend.to('http://www.theCatalog.com/?lang=de'); } break; case 'zh': if (url !== 'http://www.theCatalog.com/?lang=zh') { wixLocationFrontend.to('http://www.theCatalog.com/?lang=zh'); } break; case 'es': if (url !== 'http://www.theCatalog.com/?lang=es') { wixLocationFrontend.to('http://www.theCatalog.com/?lang=es'); } break; // If locale is set to annother language, default is English. // Also, handle the case where no language code is in the URL. default: if (url !== 'http:///?lang=' && url !== 'http://') { wixLocationFrontend.to('http://www.theCatalog.com/?lang=en'); } break; } // The timeout value is 6500 nanoseconds. }, 6500); }); ```
**Notes** * In line 48, the sample code defaults to English if the locale is not French, German, Chinese, or Spanish.   * We also make sure that the page does not reload if no language code is specified in the URL.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Sending Tracking and Analytics Events ## Article: Sending Tracking and Analytics Events ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/sending-tracking-and-analytics-events.md ## Article Content: # Velo Tutorial: Sending Tracking and Analytics Events Adding tracking code to your site allows you to monitor how users interact with your site. Collecting tracking data gives you insight into their online behavior and helps you to optimize your online marketing strategies. In this article, we demonstrate how to send a tracking event from your page code. We start by connecting our site to an analytics tool. Then, we set up a simple page. Finally, we add code so that a tracking event is sent when a user performs a specific interaction. ### Tracking & Analytics Tools We begin by connecting the desired tracking and analytics tools to our site. Currently, you can send events through code to Google Analytics and Facebook Pixel.
**Note** Tracking & Analytics requires you to have an account with an external analytics tool and only works with premium sites.
In our example, we've connected our site to Google Analytics. ### Page Setup You set up your page as you normally would. However, you'll need to decide when you want to send tracking events. In our example, we send an event when a user clicks a button to download a document. On our page we have a repeater that displays a list of documents stored in a collection. When one of the **Download** buttons is clicked, we send a custom Document Download event that contains the name of the document that was downloaded.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/04/24/3d8542e0-0aa7-4529-830d-f34466aad689.png)
On our page we have the following elements: |Type |ID |Usage | |---|---|---| |Text|docTitle|Display the document title |Button|downloadButton|Download the document |Repeater|docRepeater|Display a title and download button for each document |Dataset|docDataset|Connecting elements to document data stored in a collection The repeater, button, and text elements are connected to the dataset so that the repeater displays the title of each document and a button to download the document.  ### Tracking Code You can add the code to send a tracking event wherever it suits your needs. Most often, you will add it in an event handler that responds to a user interaction, such as a button click. In our example we want to track each time a user clicks the download button. So, we add the tracking code to an [event handler wired](https://support.wix.com/en/article/working-with-events-in-wix-code) to the download button. First, we import `wixWindowFrontend` at the top of our page code. ```javascript import wixWindowFrontend from 'wix-window-frontend'; ``` Then, we wire an event handler to the download button's click event. ```javascript export function downloadButton_click(event) { // This function was added from the Properties & Events panel. To learn more, visit http://wix.to/UcBnC-4 // Add your code for this event here: } ``` Finally, we call the `trackEvent()` function. Here, we send a custom event that indicates a download has occurred. We also grab the title of the document that was downloaded and add it to the event. When sending a custom event to Google Analytics, use the following parameters: |Key |Value Type |Required |Usage | |---|---|---|---| |eventCategory|string|yes|Object that was interacted with |eventAction|string|yes|Type of interaction |eventLabel|string|no|Event category |eventValue|integer|no|Numeric value associated with the event ```javascript wixWindowFrontend.trackEvent("CustomEvent", { "event": "Document Download", "eventCategory": "Downloads", "eventAction": "Download", "eventLabel": $w('#docDataset').getCurrentItem().title } ); ``` * * * Putting it all together, our code looks like this: ```javascript import wixWindowFrontend from 'wix-window-frontend'; export function downloadButton_click(event) { wixWindowFrontend.trackEvent("CustomEvent", { "event": "Document Download", "eventCategory": "Downloads", "eventAction": "Download", "eventLabel": $w('#dataset1').getCurrentItem().title } ); } ``` When a user clicks the download button, we'll see an event registered in our analytics tool. 
### Learn More To learn more about the types of events you can send and their corresponding parameters, see [trackEvent( )](https://www.wix.com/velo/reference/wix-window/trackevent) in the API Reference. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Displaying a Pro Gallery on Your Site Using the Pro Gallery Backend API ## Article: Displaying a Pro Gallery on Your Site Using the Pro Gallery Backend API ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/displaying-a-pro-gallery-on-your-site-using-the-pro-gallery-backend-api.md ## Article Content: # Velo Tutorial: Displaying a Pro Gallery on Your Site Using the Pro Gallery Backend API The [Velo Pro Gallery Backend API](https://dev.wix.com/docs/velo/api-reference/wix-pro-gallery-backend/introduction.md) allows you to create and manage pro galleries on your site's backend. When you add a pro gallery element to your site, a corresponding pro gallery is automatically created on your site's backend. However, when you use the Pro Gallery backend API to create a pro gallery on your site's backend, you need to do a few steps to display it on your site.  You may want to display a pro gallery from your site's backend on your site, for example, if you have a page with a pro gallery element where you want to display different galleries for each site member who logs in to your site. You can use this API to store the pro galleries in the backend instead of overloading the frontend. Then for each site member who logs in, you can export the relevant gallery from the backend. This article describes how you can choose an existing pro gallery on your site's backend, connect it to a pro gallery element on your page, and display it on your site. We start by calling the [listGalleries()](https://www.wix.com/velo/reference/wix-pro-gallery-backend/progallery/listgalleries) function to get a list of all the pro galleries on our site's backend. Then we choose the pro gallery we want to display on our live site. To export the gallery we want from our site's backend, we call the [getGallery()](https://www.wix.com/velo/reference/wix-pro-gallery-backend/progallery/getgallery) function with the ID of the pro gallery that we chose. To use the exported backend gallery on our frontend, we need to first write code to convert the backend gallery items to the frontend [$w() Gallery](https://www.wix.com/velo/reference/$w/gallery/currentitem) items format. This is because currently the backend gallery API and the frontend $w() Gallery API have different formats. Last, we import our converted items to our page code, and set the items to a particular gallery element on our page. This temporarily overrides the frontend gallery element's items with the converted backend gallery items. Once the site is published, the converted pro gallery items are visible on our live site. >**Note** > This example assumes you used the [createGallery()](https://www.wix.com/velo/reference/wix-pro-gallery-backend/progallery/creategallery) API to create one or more pro galleries in site's your backend. ### Before You Start Before you start working with the Wix pro gallery in code, make sure to [add a Pro Gallery to your site](https://support.wix.com/en/article/wix-pro-gallery-adding-and-setting-up-your-gallery#step-1-add-wix-pro-gallery-to-your-site). In the steps below, we'll first take a look at the code piece by piece to understand what it's doing.
**Important** Note the following limitations when converting backend gallery items to the frontend [$w() Gallery](https://www.wix.com/velo/reference/$w/gallery/currentitem) items format: * Backend galleries that contain text items can't be imported to your page code. Trying to do so throws an error, and breaks the gallery on your live site. This is because text items can't be converted to the frontend gallery items format, as the frontend [$w() Gallery](https://www.wix.com/velo/reference/$w/gallery/currentitem) items currently only support image and video types.  * Backend galleries items that contain a link with text don't appear on your site. This is because the text property in the item's link object can't be converted to the frontend gallery items format, as the frontend [$w() Gallery](https://www.wix.com/velo/reference/$w/gallery/currentitem) items object doesn't contain a link with text property.
### Step 1: List All Pro Galleries on the Backend In our example, we import the Pro Gallery API from the wix-pro-gallery-backend module. We then call the [listGalleries()](https://www.wix.com/velo/reference/wix-pro-gallery-backend/progallery/listgalleries) function to get a list of all existing galleries in our site's backend.  Here is sample backend code that we put in a **web module** (.web.js file): ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { proGallery } from 'wix-pro-gallery-backend'; export const myListGalleriesFunction = webMethod(Permissions.Anyone, async () => { try { const galleryList = await proGallery.listGalleries(); return galleryList; } catch (error) { console.error(error); // Handle the error } }); ``` **Line 1**: First, we import the pro gallery API from the wix-pro-gallery-backend module.  **Lines 3-6**: Next we create and export the myListGalleriesFunction() that awaits and returns the listGalleries() function.  **Lines 7-11**: We catch and handle any potential errors.
**Important** The function in this web module is used for functional testing only. If you don't want to expose all your backend gallery contents, [change the function's permissions to "Admin"](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions).
### Step 2: Select a Gallery to Display on Live Site Next we use [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/about-functional-testing.md) to run the code above. The code returns list of backend galleries. We then select the gallery we want to display on our live site, recording its ID to be able to identify it in a later step. In our case, we want the ID shown below. Your list of backend galleries should look something like this: 
Now that we know which gallery we want to use, we create a new function. This function calls the getGallery() function with the ID of the gallery that we chose. It then returns the gallery object that we selected.  ```javascript export const mySelectGalleryFunction = webMethod(Permissions.Anyone, async () => { const selectedGalleryId = "63ffd8e7-101b-49be-9498-00c2e6f5b65c"; try { const selectedGallery = await proGallery.getGallery(selectedGalleryId); // Function continues below ``` **Line 1**: We create and export the mySelectGalleryFunction() that awaits the getGallery() function. **Lines 2-6**: We declare the gallery ID that we chose in step 1 as a variable, and pass it as a parameter in the getGallery() function. ### Step 3: Convert Backend Gallery Items to Frontend Format We need to take the backend gallery that we chose, and convert the items to the frontend [$w() Gallery](https://www.wix.com/velo/reference/$w/gallery/currentitem) items. ```javascript // Function continued from above // Convert the selected backend gallery's items to the frontend gallery items format. const convertedItems = selectedGallery.items.map(item => convertToFrontend(item)); return convertedItems; } catch (error) { console.error(error); // Handle the error } }); function convertToFrontend(item) { return { slug: item._id, type: item.type, src: item.image ? item.image.imageInfo : item.video.videoInfo, description: item.description, title: item.title, link: item.link ? item.link.url : undefined } } ``` **Lines 1-6**: We map the selected backend gallery object's items to the frontend gallery object's items. We do this by calling the convertToFrontend() function that we create later on. Then we return these converted items.  **Lines 7-12**: We catch and handle any potential errors. **Lines 13-23**: We create a function to convert the backend gallery items to the frontend gallery items. Note that the src is either image or video only, as the frontend $w() Gallery object doesn't support text items. ### Step 4: Import Converted Gallery Items to our Page Code To bring the converted gallery items to the frontend, we need to import the mySelectGalleryFunction function to our page code. We add a button element to our page, and call the imported mySelectGalleryFunction() function on button click. Then we set the frontend gallery element's items to the converted gallery items.  We add the following code to our **page code**:  ```javascript import { mySelectGalleryFunction } from 'backend/myWebModuleFile.web'; $w('#galleryButton').onClick(async () => { try { // Call the mySelectGalleryFunction function to select the backend gallery and convert the backend gallery's items to the frontend items format. const newlyConvertedGalleryItems = await mySelectGalleryFunction(); // Set the frontend gallery element's items to the converted backend gallery items. $w("#myGalleryElement").items = newlyConvertedGalleryItems; } catch (error) { // Gallery not created console.error(error); } }); ``` **Line 1**: First, we import the mySelectGalleryFunction function from our module (myWebModuleFile.web.js file).  **Line 2-6**: We add an onClick() event handler to the button element on our page. In the event handler, we call the mySelectGalleryFunction() function. **Lines 7-9**: Still in the event handler, we set the frontend gallery element's items to the newly converted gallery. This occurs when the button is triggered.  **Lines 10-15**: We then catch and handle any potential errors.
**Important** Setting the frontend gallery element's items to the converted gallery items temporarily overrides the frontend gallery element's items with the converted backend gallery items. However, the pro gallery element's ID remains the same. For example, if you call the [getGallery()](https://www.wix.com/velo/reference/wix-pro-gallery-backend/progallery/getgallery) function with the gallery element's ID, the original gallery element's items are returned, and not the converted backend gallery items.
### All The Code Here is the complete **backend code** (.web.js file) for this example: ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { proGallery } from 'wix-pro-gallery-backend'; export const myListGalleriesFunction = webMethod(Permissions.Anyone, async () => { try { let galleryList = await proGallery.listGalleries(); return galleryList; } catch (error) { console.error(error); // Handle the error } }); export const mySelectGalleryFunction = webMethod(Permissions.Anyone, async () => { const selectedGalleryId = "63ffd8e7-101b-49be-9498-00c2e6f5b65c"; try { const selectedGallery = await proGallery.getGallery(selectedGalleryId); // Convert the selected backend gallery's items to the frontend gallery items format. const convertedItems = selectedGallery.items.map(item => convertToFrontend(item)); return convertedItems; } catch (error) { console.error(error); // Handle the error } }); function convertToFrontend(item) { return { slug: item._id, type: item.type, src: item.image ? item.image.imageInfo : item.video.videoInfo, description: item.description, title: item.title, link: item.link ? item.link.url : undefined } } ``` Here is the complete **page code** for this example: ```javascript import { mySelectGalleryFunction } from 'backend/myWebModuleFile.web'; $w('#galleryButton').onClick(async () => { try { // Call the mySelectGalleryFunction function to select the backend gallery and convert the backend gallery's items to the frontend items format. const newlyConvertedGalleryItems = await mySelectGalleryFunction(); // Set the frontend gallery element's items to the converted backend gallery items. $w("#myGalleryElement").items = newlyConvertedGalleryItems; } catch (error) { // Gallery not created console.error(error); } }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Changing the Direction of Menu Tabs Based on the Current Language ## Article: Changing the Direction of Menu Tabs Based on the Current Language ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/changing-the-direction-of-menu-tabs-based-on-the-current-language.md ## Article Content: # Velo Tutorial: Changing the Direction of Menu Tabs Based on the Current Language >**Note:** > This tutorial demonstrates how to use Velo to show and hide different menus in a multilingual site, based on the currently selected language. However, you can use the instructions in this tutorial to customize your page's layout and show or hide any other elements as well, based on the currently selected language. You can use Velo to display menu tabs either "right-to-left" or 'left-to-right" based on the selected language. Site visitors select the language from a language selector dropdown. For example, choosing a language that is read from right-to-left, such as Hebrew, displays the menu options from the right to the left. In the example below, ״צור קשר״  is displayed on the left of the menu, even though in English, it means "Contact Us," which is displayed on the right. This shows that the order of the tabs is based on the language selection.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2019/08/25/00a9b6b2-dd1d-44d4-8c28-fd65e11352bb/59b425e9-9a05-423d-b28b-bd3a74579a1c.png)
This tutorial has 2 parts: * Instructions for adding menus and ordering menu tabs to your site. * Instructions on how to set up code you can copy and paste onto your page or site, including an explanation of what each line of code does. > > This tutorial assumes you enabled [Wix Multilingual](https://support.wix.com/en/article/using-wix-multilingual-to-create-a-multilingual-site) on your site, and set up the languages you need. ### Adding Menus for Each Language We will add a menu for each language. To best demonstrate how tab direction works, this tutorial adds horizontal menus. These menus are usually placed in the header or footer of a site.  1. Add a menu for each language. In the top left, click on **Add Elements** > **Menu**. For this tutorial, select one of the horizontal menus. You can place one menu element on top of the others, or have menu elements overlap, because we are going to display only one menu at a time. 2. To change the direction of each menu's tabs to either **Left to Right** or **Right to Left**, click **Layout**.
**Tip:** For languages that read **Right to Left**, you should also change the alignment of the text to right-aligned under **Menu Layout** > **Text Alignment**.
### Coding the Redirection of Menu Tabs The sample code below checks the language the user selected using [Wix Multilingual'](https://support.wix.com/en/article/using-wix-multilingual-to-create-a-multilingual-site)s language selector dropdown element and then shows only the menu in the correct language with the correctly-ordered tabs. All other menus are hidden. #### Instructions 1. Open the Code panel. + **Wix Studio:** Click the ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/b5b2d7b8-0a53-4239-9e87-0e72bd8189b8) on the left sidebar. The [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) appears at the bottom of the page. You can drag it up to adjust the size. + **Wix Editor:** If [Dev mode](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) is enabled, the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) appears at the bottom of the page. You can drag it up to adjust the size. 2. Copy the code below and paste it either in the page tab that has your menus, or in **masterPage.js** located in the Page Code section of the Code sidebar (Wix Studio), or the Velo sidebar (Wix Editor). 3. In the code, make sure to substitute your own menu IDs from the Properties & Events panel (you can hover over the element to see the ID) and the [language codes](https://support.wix.com/en/article/languages-available-in-wix-multilingual) used on your site. 4. Preview or publish the page. ```javascript import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { let myLang = wixWindowFrontend.multilingual.currentLanguage; if (myLang === 'he') { $w('#LTREnglishMenu').hide(); $w('#RTLHebrewMenu').show(); } else { $w('#RTLHebrewMenu').hide(); $w('#LTREnglishMenu').show(); }; }); ``` #### Understanding the Code **Line 1**: Import the `wix-window-frontend` API for Wix Multilingual. **Line 2**: Add your code to the `onReady()` function to make sure all elements are loaded and available. **Line 3**: Use the `currentLanguage` property to determine what language the site visitor chose from the language selector. **Lines 4-6**: If the language is Hebrew, show the Hebrew menu, with tabs ordered from right-to-left. **Lines 7-9**: If the language is not Hebrew, show the English menu, with tabs ordered from left-to-right. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating a One-Time Popup ## Article: Creating a One-Time Popup ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/creating-a-one-time-popup.md ## Article Content: # Velo Tutorial: Creating a One-Time Popup > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. In this article we demonstrate how to create a popup that appears the first time a visitor visits your site. We use a popup to create the experience and we use the `wix-storage-frontend` API to determine if the current visitor has already seen the popup. ### Popup We begin by adding a popup to the site. When you add a popup, it appears in the **Popups** section of your site pages. We don't want the popup to show for site visitors unless we specifically open it using code. To make sure the popup doesn't open automatically, select the popup and click the **Set Triggers** button. Set the popup so that it doesn't automatically display on pages. While the popup is still selected, click **Set Triggers** to set the **Popup Settings**. Here you can give your popup a meaningful name. In this example, we name our popup **Announcement**. The name will be used later in the code to open the popup using the [Lightbox API](https://dev.wix.com/docs/velo/apis/wix-window-frontend/lightbox/introduction.md). Now you can design the popup to fit your needs. You can add, delete, and modify any of the elements that are contained in the popup. You can also change the layout and design of the popup. ### Code The code works using the following model. Each time a visitor visits your site we check the visitor's browser storage for a flag that we set the first time a visitor visits your site. If we don't find the flag, we know this is the visitor's first time visiting the site. So we show the popup and set the flag for the next time the visitor visits the site. If we find the flag, we know the visitor has visited the site already and we don't show the popup. The following code is added to the **Global Code** section of the Code sidebar (Wix Studio), or to the **masterPage.js** file located in the **Page Code** section of the Code sidebar (Wix Editor). Because the code is shared across all of your pages, it doesn't make a difference which page you are viewing when you add the code. #### Imports In this example we use the `wix-storage-frontend` API to store a value that tells us whether a visitor has seen the popup already. The `wix-storage-frontend` API uses the storage of a visitor's browser. We also use the `wix-window-frontend` API to open the popup. So our importing code looks like this: ```javascript import {session} from 'wix-storage-frontend'; import wixWindowFrontend from 'wix-window-frontend'; ``` #### onReady( ) In the `onReady()` event handler, we check to see if the `firstTimePopupShown` key exists in the user's browser storage. The name of the key we check and set doesn't really matter. We just need to be consistent and make sure we check the same key that we set. If the key doesn't exist, we open the popup using the popup's name. We also set a value for the `firstTimePopupShown` key so the key will be found the next time the user visits any of the site's pages. So our `onReady` code looks like this: ```javascript $w.onReady(function () { // flag is not found if(!session.getItem("firstTimePopupShown")) { // open lightbox (popup) wixWindowFrontend.openLightbox("Announcement"); // set flag for future visits session.setItem("firstTimePopupShown", "yes"); } } ); ``` > **Note:** If you've named your popup anything other than **Announcement**, you need to edit the code above to reflect your popup's name. ### Customization In this example, we've created a popup that appears the first time a visitor visits any page in your site during a single browsing session. With some minor adjustments, you can customize the example to behave in a different manner as described below.   #### Session Storage vs. Local Storage In this example, we used **session** storage to hold the flag that tells us if a visitor has seen the popup already. Session storage persists as long as the visitor's web session is active. The session ends when the visitor closes the browser tab or window. That means, using session storage, we show the popup the first time the visitor views the site for each individual session. If the visitor closes the browser tab and then visits the site again in a different tab, the popup will be shown again. If you want to show the popup only once, regardless of whether a visitor is in the same browser tab or a new one, you can use **local** storage. Local storage never expires, even if a visitor closes your page. That means, using local storage, we show the popup the first time a visitor views the site. If a visitor closes the browser tab and then visits the site again in a different tab, the popup will not be shown again. To change the code above so that it uses local storage instead of session storage, change all the instances of `session` to `local`. That means the code should look like this: ```javascript import {local} from 'wix-storage-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(function () { if(!local.getItem("firstTimePopupShown")) { wixWindowFrontend.openLightbox("Announcement"); local.setItem("firstTimePopupShown", "yes"); } } ); ``` #### Site vs Page In this example, we placed our code in the **Global Code** section of the Code sidebar (Wix Studio), or the **masterPage.js** file in the **Page code** section of the Code sidebar (Wix Editor). Code in **masterPage.js** runs on all the pages in a site. That means the popup will appear on the first page a visitor visits, regardless of which page it is. If you want the popup to appear the first time a visitor visits a specific page, instead of any page on a site, move the code above into the page code for the page you want the popup to appear on. ### API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). * [wix-storage-frontend](https://www.wix.com/velo/reference/wix-storage)  * [wix-window-frontend](https://www.wix.com/velo/reference/wix-window.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using the Upload Button with Code ## Article: Using the Upload Button with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-the-upload-button-with-code.md ## Article Content: # Velo Tutorial: Using the Upload Button with Code
Visit the Velo by Wix website to onboard and continue learning.
## File Upload Scenario The typical file upload scenario is a two-part process. First your site visitor chooses the file to upload and then they trigger the actual upload. This usually requires at least two page elements, an upload button element and a regular button. The site visitor chooses which file to upload by clicking the upload button and selecting the file in a native file chooser dialog. (The site visitor can only choose from the specified file type.) Then the site visitor clicks the regular button to trigger some code that performs the upload.
**Note** The file types of the images, documents, videos, and audios that can be uploaded are listed [here](https://support.wix.com/en/article/supported-media-file-types-and-file-sizes).
## Upload Example Let's create a simple form that uploads an image to better understand how this process works. We'll add elements to a page, wire some elements to a bit of code, and then test the form. ### Prerequisites Before you create an upload form, you'll need to be somewhat comfortable with JavaScript code, especially [event handlers](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) and Promises. ### Add Page Elements Add the following elements to the page: * An upload button for choosing a file. The upload buttons are found in the **Input** section of the **Add Elements** menu. * A regular button to trigger the upload code. * A text element to display messages to the site visitor. * An image element. Any image is fine. We're only using it as a placeholder for the uploaded image. Feel free to change the design and text of any of the elements we've just added. The complete form should look something like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/06/05/a98afa4f-ed3b-49fd-9f7c-138189ecdd73.png)
### Wire Upload Code The upload button takes care of the site visitor choosing the file for us. But we have to wire code to the regular button to perform the actual upload. We'll start by [adding](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) an onClick event handler for the button. Next, we'll add the code that performs the upload. In the onClick event handler add the following code so the full function is as follows:  ```javascript export function button1_click(event) { if($w("#uploadButton1").value.length > 0) { $w("#text1").text = "Uploading " + $w("#uploadButton1").value[0].name; $w("#uploadButton1").uploadFiles() .then( (uploadedFiles) => { $w("#text1").text = "Upload successful"; $w("#image1").src = uploadedFiles[0].fileUrl; }) .catch( (uploadError) => { $w("#text1").text = "File upload error"; console.log("File upload error: " + uploadError.errorCode); console.log(uploadError.errorDescription); }); } else { $w("#text1").text = "Please choose a file to upload."; } } ``` Let's take a look at this code piece by piece to understand what it's doing. First, on line 2, we check to see if the site visitor has chosen a file. We did not specify a file type, so "image" is assumed. `if($w("#uploadButton1").value.length > 0) {` The `value` property of the upload button returns an array of `File` objects, each representing a file the site visitor has chosen to upload. So we check its `length` to see if this array has at least one item in it, meaning the site visitor has chosen at least one file. * * * If the array returned by `value` is empty, we move to the `else` on lines 15-17 to display a message to the site visitor in the text element. ```js else { $w("#text1").text = "Please choose a file to upload."; } ``` That ends the upload attempt. The site visitor can then choose a file and start the upload again. If the array has a `File` element in it, meaning the site visitor chose a file to upload, we continue with the upload process. * * * On line 3, we start the upload process by displaying a message to the site visitor that we are uploading the chosen file. `$w("#text1").text = "Uploading " + $w("#uploadButton1").value[0].name;` * * * Next, on line 4, we call the upload button's `uploadFiles()` function, which performs the actual upload. `$w("#uploadButton1").uploadFiles()` It returns a Promise that resolves to an array of `UploadedFile` objects with information about the uploaded files if the upload was successful or rejects to an `UploadError` object with information about the error that occurred if the upload was not successful. * * * If the upload is successful, the Promise continues with the `then()` on lines 5-8. ```js .then( (uploadedFiles) => { $w("#text1").text = "Upload successful"; $w("#image1").src = uploadedFiles[0].fileUrl; }) ``` Here we display a message to the site visitor saying that the upload was successful and we display the uploaded image. We display the image by getting the `fileUrl` property of the first `UploadedFile` object in the array, which returns the Wix URL where the uploaded file is stored. * * * If the upload is unsuccessful, the Promise rejects with the `catch()` on lines 9-13. ```js .catch( (uploadError) => { $w("#text1").text = "File upload error"; console.log("File upload error: " + uploadError.errorCode); console.log(uploadError.errorDescription); }); ``` Here we display a message to the site visitor saying that the upload was not successful. We also log the error information from the `uploadError` object using its `errorCode` and `errorDescription` properties. ### Testing Now we can test our form by publishing the site and attempting to upload an image.  First, on the live site, let's try to click the **Start Upload** button before we choose a file. We should see the status line change to **Please choose a file to upload**. Next, let's use the upload button to choose a file and then click the **Start Upload** button. We should see the status line change to **Uploading some\_file.png** and then after a few moments change again to **Upload successful**. The image we uploaded should also be displayed on the page. Finally, let's return to the Editor and find the uploaded file. Select the upload button and click **Settings**. In the Button Settings panel, scroll down to **Manage visitor files** and click **View Uploaded Files**, which opens the images that have been received by your site. There you should see the image you just uploaded. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Hiding a Video Player When There Is No Video to Play ## Article: Hiding a Video Player When There Is No Video to Play ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/hiding-a-video-player-when-there-is-no-video-to-play.md ## Article Content: # Velo Tutorial: Hiding a Video Player When There Is No Video to Play When you [connect](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-the-single-video-player) a Video Player element to a URL field in your collection, when the page is viewed, the player displays the associated video. If there is no URL for a given item, the video player displays the default video defined in its [Social Video Settings panel](https://support.wix.com/en/article/social-video-settings). If you'd rather not have any video play, you can automatically hide the Video Player when the item in your collection has no URL. This tutorial shows you how to do this using [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md). This tutorial has 2 parts: * Instructions on how to get set up, including code you can copy and paste onto your page * An explanation of what each line of code does ### Instructions >**Before you begin:** > Make sure you have a [collection](https://support.wix.com/en/article/about-database-collections) that has a field of type "URL" where you store the video locations. There should be at least one item that has the URL field blank. 1. Add a [dataset](https://support.wix.com/en/article/about-datasets-and-connecting-data) to your page and connect it to your collection. 2. Add a Video Player and [connect](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-the-single-video-player) it to the URL field. 3. Open the code editor for your page. 4. Copy and paste the code below. ```javascript $w.onReady(() => { $w("#myDataset").onReady(() => { // Gets the current item properties and stores them in a variable called item const item = $w("#myDataset").getCurrentItem(); // Checks if the current item has a value in the "video" field if (!item.video) { // Collapses the video player if there is no value for "video" $w("#videoPlayer").collapse(); } }); }); ``` 1. Make sure to make these replacements: * _#myDataset_: the ID of the dataset * _video_: the [field ID](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields#field-id-velo-by-wix-only) of the field in your collection that stores the video URL * _#videoPlayer_: the ID of the video player 2.  Preview your page for an item that does not have a URL and check that the video player is not displayed. >**Note:** Open the Properties & Events panel to see and update the IDs of your elements. ### Understanding the Code This section explains the main lines of the code. Each function has a link to its more detailed explanation in the [wix-dataset API](https://www.wix.com/velo/reference/wix-dataset.html). Line 1 calls the [`onReady()`](https://www.wix.com/velo/reference/$w.html#onReady) function. This defines the code that will run when the page is finished loading. $w.onReady(() => { Line 2 calls the [`dataset onReady()`](https://www.wix.com/velo/reference/wix-dataset/dataset/onready) function. This defines the code that will run after the dataset has loaded all the information for the current item. $w("#myDataset").onReady(() => { Line 4 defines a variable called `item` and then sets its value to be the object returned by the [`getCurrentItem()`](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitem) function. const item = $w("#myDataset").getCurrentItem(); This object contains the properties (fields) in the current item and their respective values. When a given field is blank in the current item, the object does not contain the corresponding property. Line 6 checks to see if the `item` variable from the previous line includes the `video` field, which is the field in the collection that stores the video URL. if (!item.video) { If the current item's `video` field is empty, then that field is not included in the object that was returned by `getCurrentItem()` in the previous line. Line 8 collapses the video player if the `video` field is blank. $w("#videoPlayer").collapse(); If the `video` field is not blank, then this line does not run. The video player remains visible to show the video at the URL defined in your collection for this item. You could also use the [`hide()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/video/hide.md) function to hide the video player instead of collapsing it. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Send Emails Using the SendGrid npm Package ## Article: Send Emails Using the SendGrid npm Package ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md ## Article Content: # Velo Tutorial: Send Emails Using the SendGrid npm Package
>**Note:** > This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#finding-your-way). This article describes how to integrate your Wix site with the [SendGrid service](https://sendgrid.com/) and send emails directly from your site. You'll need to add the [SendGrid npm package](https://www.npmjs.com/package/@sendgrid/mail) to your site to incorporate the SendGrid functionality. This tutorial is based on the SendGrid npm Integration [example site](https://www.wix.com/velo-examples/sendgrid-npm). You can open a copy of the [site's editor](https://editor.wix.com/html/editor/web/renderer/new?siteId=5494404c-1163-4c51-8bc2-bf8466a23e34&metaSiteId=1e3b3a34-4b1e-416c-abb9-334db84d0261&autoDevMode=true) and play around with the code. >**Notes** > * If you want to work with a copy of the example site, you'll need to create your own SendGrid account and save your email address and the SendGrid API key in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) (see detailed instructions [below](https://support.wix.com/en/article/velo-tutorial-send-emails-using-the-sendgrid-npm-package#step-2a-store-secrets-in-the-secrets-manager)). > * You can also integrate your Wix site with SendGrid using the [Fetch API](https://www.wix.com/velo/reference/wix-fetch). ## Overview To set up your site to send emails with the SendGrid service, you'll need to perform the following steps: 1. Create a SendGrid account. 2. Store the SendGrid API key and your sender email address in the Secrets Manager. 3. Add the SendGrid npm package to your site. 4. Add a custom form to your page for getting the recipient email address and email text from site visitors.  5. Write code in a backend web module to get the API key from the Secrets Manager and send an email using an npm package function. 6. Write page code to use the imported backend function to send an email, using the email data entered in the form fields. >**Note** > This tutorial describes how to use a custom form to get email data such as email addresses and body text from site visitors. You might have a different use case, such as constructing the emails yourself as a site admin. You might use email data stored in a database collection, enter the email data directly in your code, or use the custom form but on a [Dashboard page](https://support.wix.com/en/article/velo-working-with-dashboard-pages). In any case, you'll need to adjust your code to get the email data from its source and pass it to the [sendEmail()](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) function. ## Step 1: Create an Account with SendGrid To use the SendGrid service on your Wix site, you'll need to create a [SendGrid account](https://signup.sendgrid.com/). The email address you verify in the account will be the sender address for the emails you send from your Wix site. Once you've created an account, do the following: 1. In the SendGrid site, navigate to the left menu. 2. Go to **Settings > Sender Authentication** and verify your email address. 3. Go to **Settings > API Keys** and generate an API key.  4. Copy the key from your dashboard and store it in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) in your Wix site (see the next step).   ## Step 2: Set Up the Site To set up your Wix site, do the following: 1. Store secrets in the Secrets Manager. 2. [Add the SendGrid npm package to your site.](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) 3. Add elements to your page. ### Step 2a: Store Secrets in the Secrets Manager For security purposes, it's best to store sensitive content such as API keys in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md).  1. In the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#ways-to-code-with-wix-studio), select the **Developer Tools** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/14/5526a3fb-4472-4c9e-9cf2-a7231343d9c6/c54e27bf-b090-4b15-adc0-6a437d141721.png)  tab.  2. Under the Security section, select **Secrets Manager**. 3. In the top right, click **Store Secret**. 4. Store both your SendGrid API key and the verified email address associated with your SendGrid account in a single secret. The secret should look like this: * Make sure to name the secret: **sendGridSecret**. * Replace `` with the value of your API key surrounded by quotation marks. * Replace `` with your email address surrounded by quotes. * Your secret value will look something like this: ```json { "key": "AB.CdPEfg1HIJk-LMnoPq2R3s.T4uVwxyZAbcd567eF8fghIjKL9mN", "senderEmail": "janedoe@example.com" } ``` 5. Click **Save**. Later you'll use the [Secrets API](https://www.wix.com/velo/reference/wix-secrets-backend/getsecret) to extract the secret and use it securely in your code (see [Step 3b](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md)). ### Step 2b: Add the SendGrid npm Package Use the [Package Manager](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) to add the SendGrid npm package to your site: 1. In the Code sidebar, click the **Code Files** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/78515cd5-9279-4fb1-a136-6a07589a5aaa/5a63551d-467b-4d54-b039-5588426d4ff7.png)  tab.  2. In the Packages section, hover over **npm**, click the plus icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/04083fa4-3262-4606-98d1-cbc359bf8585/1a26226e-c281-42d2-8a60-88e44fe40c61.png)  and select **Install package from npm**. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/dce8e3fb-026d-4605-b51c-c8b552f0e50d/6d6bc9e2-6b26-4d8d-a3b4-f3283469ec0d.png) The Package Manager opens. 3. In the search bar, enter '**sendgrid**'. 4. Next to the **@sendgrid/mail** package, click **Install**. You'll see the package under npm in the Packages section of your Code Files tab in the Code sidebar. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/13/411a1411-8247-4e53-86c0-6f21bdbe4db6/24bfd60a-d671-42b5-b611-65579ab2a1e2.png) Now you can import the package and use its functionality in your code. ### Step 2c: Add Page Elements To create a custom form, add the following elements to your **Home** page: * Input elements for the recipient's email and subject line * Text box for the email body text * Submit button for sending the email * Text element for displaying success and error messages ## Step 3: Write Backend Code in the sendEmail.web.js Web Module Since backend code is more secure, we wrote code to implement the following functionality in a [backend web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md):  1. Extract the SendGrid API key from the Secrets Manager. 2. Set the API key in SendGrid. 3. Send an email using a SendGrid npm package function. >**Note** > To see all the code for this example in a single block, [scroll down](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md) to the end of the tutorial. ### Step 3a: Add a Web Module [Web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) are exclusive to Velo. In a web module you can write functions that run in the backend and are easily called in your frontend code. To add a web module: 1. In the Code sidebar, select the **Code Files** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/21/f71f916a-a0b5-4718-bacd-4fa395f0f77d/c0ef9144-9590-4f94-9c03-4392ecd5923f.png)  tab. 2. In the Backend section, click **Add web module**. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/10/21/03237b75-04f7-4941-85a3-75f37225092c/ced5cce5-83d6-42ca-9c55-0edec8d5f754.png) 3. Name the file **sendEmail.web.js**. The file will open in the [code editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). Place the code below in the file. ### Step 3b: Import Modules Start by importing the modules you'll need: ```javascript import { Permissions, webMethod } from 'wix-web-module'; import sgMail from "@sendgrid/mail"; import wixSecretsBackend from 'wix-secrets-backend'; ``` #### Understanding the Code **Line 1**: Import the `Permissions` enum and `webMethod` function from `wix-web-module`. **Line 2**: Import the `sgMail` module containing the functions you need from the npm package that we added to our site. **Line 3**: Import the `wix-secrets-backend` module for extracting secrets from the Secrets Manager. ### Step 3c: Extract and Set the API Key In our `sendEmail` function, we start by getting the SendGrid API key and sender email address that we saved in the Secrets Manager, and setting the API key to enable usage of the SendGrid service. ```javascript export const sendEmail = webMethod(Permissions.Anyone, async (recipient, subject, body) => { const sendGridSecret = JSON.parse(await wixSecretsBackend.getSecret('sendGridSecret')); const key = sendGridSecret.key; const senderEmail = sendGridSecret.senderEmail; sgMail.setApiKey(key); // Function continues below ``` #### Understanding the Code **Line 1**: Declare and export the asynchronous `sendEmail` function for sending the email. **Line 3**: Get the SendGrid secret from the Secrets Manager using the `getSecret` function and save it in a variable named `sendGridSecret`. The `JSON.parse` function converts the JSON string into an object. **Line 4**: Save the API key extracted from the secret as a variable called `key`. **Line 5**: Save the sender email extracted from the secret as a variable called `senderEmail`. **Line 7**: Set the API key using the `setApiKey()` function from the SendGrid npm module. ### Step 3d: Send an Email While still inside our `sendEmail()` function, we call the npm package `send()` function to send the email. ```javascript // Function continued from above const msg = { from: senderEmail, to: recipient, subject: subject, text: body }; try { return await sgMail.send(msg); } catch (error) { console.error('Error sending the email: ' + error.message); return false; } }); ``` #### Understanding the Code **Lines 1-6**: Set the data for the email to be sent, such as the recipent's email address and the text of the email body. The sender email was extracted from the Secrets Manager and the rest of the information is entered by site visitors on the frontend via input elements. **Lines 8, 10**: Use `try` and `catch` to catch any errors. **Line 9**: Call the `send()` npm package function to send the email with the email data passed to the function. ## Step 4: Write Page Code in the Frontend >**Note** > The frontend code for this tutorial is based on the setup for this example site, a custom form exposed to site visitors. If you decide to set up your site differently, you'll need to adapt the frontend code accordingly. In our page code on the frontend of our site, we do the following: 1. Get email information such as the recipient's email address and email text from site visitors via the custom form fields. 2. Validate the form fields. 3. Use the `sendEmail()` function imported from the backend with the form field values to send an email. 4. Show a success or error message. ### Step 4a: Setup First we import the function we need from the backend and set a global constant. ```javascript import { sendEmail } from 'backend/sendEmail.web'; const SUCCESS_CODE = 202; ``` #### Understanding the Code **Line 1**: Import the `sendEmail()` function that we created in the backend so that we can use it in our page code. **Line 2**: Set a `SUCCESS_CODE` constant to `202`. We'll use it to check whether the `sendEmail()` function request was successful. ### Step 4b: Validate the Form Fields We added the `checkFormFields()` function to make sure all form field input values are valid before sending an email. ```javascript function checkFormFields() { return $w('#toEmail').validity.valid && $w('#subject').validity.valid && $w('#emailContent').validity.valid; } ``` #### Understanding the Code **Line 1**: Declare the function that we'll call in the `onReady()` function. **Line 2**: If all input values are valid, return `true`. If one is not valid, return `false`. ### Step 4c: Clear the Form Fields We added the `clearFields()` function to reset the form fields following a successful email transmission. ```javascript function clearFields() { $w('#toEmail').value = ''; $w('#subject').value = ''; $w('#emailContent').value = ''; $w('#toEmail').resetValidityIndication(); $w('#subject').resetValidityIndication(); $w('#emailContent').resetValidityIndication(); } ``` #### Understanding the Code **Line 1**: Declare the function that we'll call in the `onReady()` function. **Lines 2-4**: Clear the input values. **Lines 6-8**: Reset the inputs' visual validity indications. Some elements have a visual cue, such as a red outline, that indicates when the current value isn't valid. This function clears any indications. ### Step 4d: Show Success and Error Messages We added the `showMessage()` function to temporarily show a success or error message following an attempt to send an email. ```javascript function displayMessage(message) { $w('#messageText').text = message; $w('#messageText').show(); $w('#messageText').hide('fade', { delay: 5000 }); } ``` #### Understanding the Code **Line 1**: Declare the function that we'll call in the `onReady()` function. **Line 2**: Set the message text with the text passed to the function. **Line 3**: Show the success or error message. **Line 4**: Hide the message after 5 seconds. ### Step 4e: Send the Email when the Form Is Submitted When a site visitor clicks the **Send** button, we do the following: 1. Check whether all form field values are valid. 2. If they're valid, send the email with the values extracted from the form inputs. If the form fields aren't valid, show an error message. 3. If the email transmission request is successful, clear the form fields and display a success message. If the request fails, show an error message. ```javascript $w.onReady(function () { $w('#sendButton').onClick(async () => { const passedValidations = checkFormFields(); if (passedValidations) { const emailResult = await sendEmail( $w('#toEmail').value, $w('#subject').value, $w('#emailContent').value); if (emailResult[0].statusCode === SUCCESS_CODE) { clearFields(); displayMessage('Email was sent'); } else { displayMessage('Error sending email, please verify your SendGrid account details.'); } } else { displayMessage('Validation error, please review your input fields.'); } }) }); ``` #### Understanding the Code **Line 1**: We added our code to the `onReady()` function, which runs when the page loads. **Line 2**: When a site visitor clicks the **Send** button, run the following code. **Lines 3-4**: Check if all input field values are valid. If not, see Lines 15-16. If they are, run the following code. **Lines 5-8**: Run the `sendEmail()` function that we imported from the backend with the field input values as parameters, and assign the result to a variable. **Line 9**: Check if the email transmission request was successful (returned a `202`status code). If not, see Lines 12-13. If yes, run the following code: **Line 10**: Clear the input values and reset validity indications. **Line 11**: Display a message to let site visitors know that the email was sent. **Lines 12-13**: If the email transmission request was not successful, display an error message. **Lines 15-16**: If not all input fields are valid, display an error message. ## Learn More Check out the SendGrid npm Integration [example site](https://www.wix.com/velo-examples/sendgrid-npm) or open a copy of the [site's editor](https://editor.wix.com/html/editor/web/renderer/new?siteId=5494404c-1163-4c51-8bc2-bf8466a23e34&metaSiteId=1e3b3a34-4b1e-416c-abb9-334db84d0261&autoDevMode=true) and play around with the code. Note that if you want to work with a copy of the site, you'll need to create your own account with SendGrid and save the API key in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/send-emails-using-the-send-grid-npm-package.md).  You can also watch a [video](https://www.youtube.com/watch?v=jNI2MAno1_o), which describes the steps in this tutorial. ## Example Code Here is the complete code for this example: #### Backend Code in sendEmail.web File ```javascript import { Permissions, webMethod } from 'wix-web-module'; import sgMail from '@sendgrid/mail'; import wixSecretsBackend from 'wix-secrets-backend'; export const sendEmail = webMethod(Permissions.Anyone, async (recipient, subject, body) => { const sendGridSecret = JSON.parse(await wixSecretsBackend.getSecret('sendGridSecret')); const key = sendGridSecret.key; const senderEmail = sendGridSecret.senderEmail; sgMail.setApiKey(key); const msg = { from: senderEmail, to: recipient, subject: subject, text: body }; try { return await sgMail.send(msg); } catch (error) { console.error('Error sending the email: ' + error.message); return false; } }); ``` #### Frontend Page Code ```javascript import { sendEmail } from 'backend/sendEmail.web'; const SUCCESS_CODE = 202; $w.onReady(function () { $w('#sendButton').onClick(async () => { const passedValidations = checkFormFields(); if (passedValidations) { const emailResult = await sendEmail($w('#toEmail').value, $w('#subject').value, $w('#emailContent').value); if (emailResult[0].statusCode === SUCCESS_CODE) { clearFields(); displayMessage('Email was sent.'); } else { displayMessage('Error sending email, please verify your SendGrid account details.'); } } else { displayMessage('Validation error, please review your input fields.'); } }) }); function checkFormFields() { return $w('#toEmail').validity.valid && $w('#subject').validity.valid && $w('#emailContent').validity.valid; } function clearFields() { $w('#toEmail').value = ''; $w('#subject').value = ''; $w('#emailContent').value = ''; $w('#toEmail').resetValidityIndication(); $w('#subject').resetValidityIndication(); $w('#emailContent').resetValidityIndication(); } function displayMessage(message) { $w('#messageText').text = message; $w('#messageText').show(); $w('#messageText').hide('fade', { delay: 5000 }); } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Sending Messages with the Realtime API ## Article: Sending Messages with the Realtime API ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/sending-messages-with-the-realtime-api.md ## Article Content: # Velo Tutorial: Sending Messages with the Realtime API >**Note:** This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#finding-your-way). The Realtime API is used to send messages in realtime over channels that your site visitors are subscribed to. In this tutorial, we demonstrate the usage of the Realtime API by sending breaking news alerts to visitors on a news site. We allow visitors who are members of our site to decide what types of news alerts they receive. Alerts are sent using an admin page where the admin can choose what type of alert to send.
__Tip:__ You can see the final product using [this template](https://editor.wix.com/html/editor/web/renderer/new?siteId=f017db9d-4b4f-413b-aaaa-f71a0340e7d5&metaSiteId=bea8a2e9-ea98-4e4c-a3ad-d2a53638a0ce). To see the site's functionality in action, you need to publish the site and have it open twice. In one instance of the site you act as a site visitor or member and in the other instance you act as the admin.
### Site Overview > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. Let's take a quick look at the final product before we dive into the details of how it works. The site contains three main visitor-facing parts: the home page, subscriptions popup, and admin page. #### Home Page
The home page of our news site contains our site's title and links to news stories. We've also added two features to this page that pertain to the breaking news functionality we're building into the site. The first feature is the breaking news display. This is where we display breaking news alerts sent using the Realtime API. This feature is hidden until it is needed. The display is built from a strip with two text elements. The first text element is a label and the second one is where we populate the text of breaking news alerts. The containing strip is set to **Hidden** using the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). It is shown once a breaking news alert is received. The second feature is a settings icon. This feature is hidden if the site visitor is not logged it. It appears next to the [members area login bar](https://support.wix.com/en/article/site-members-adding-and-customizing-your-login-bar). It allows site members who are logged in to open the subscriptions popup. #### Subscriptions Popup
The subscriptions popup is where site visitors who are logged in set which types of breaking news alerts they want to receive. The list of alert types is presented using a checkbox group that is populated from data stored in a database collection. That means the list can easily be changed by simply adding or removing items in a collection. #### Admin Page
Breaking news alerts are sent using the admin page. For each alert, the admin enters the alert text, what color the alert will be shown in, and which subscribers will receive the alert. Once again, the list of alert types is presented using a checkbox group that is populated from collection data. ### Realtime API Overview The Realtime API provides functionality for sending messages from a publisher to subscribers. To send a message, the sender publishes the message on a specific channel or channel resource. To receive a message, a recipient subscribes to a channel or channel resource. Each time a sender publishes a message, all the recipients who have subscribed to that channel or channel resource receive the message. When recipients subscribe to a channel or channel resource they also define what should be done with the messages when they are received. Let's see how we apply these concepts to our news site. The publisher in our site is the admin. The admin publishes breaking news alerts. We have multiple types of alerts, such as weather alerts, political updates, and regional news. Each of these types is its own channel resource. So when the admin publishes an alert it is published on one of the channel resources. Our site's members subscribe to receive specific alerts. Each alert type that they subscribe to is one of the channel resources that the admin publishes to. So when the admin publishes a weather alert on the weather channel resource, each member that is on the site at that time and is subscribed to the weather channel resource receives the alert message.  #### Channels and Channel Resources Why do we keep talking about a channel _or channel resource_? What is the difference between a channel and a channel resource? Why would I use one over the other? These are all good questions that we'll answer now. One difference is how we identify channels and channel resources. Channels only have a name. Channel resources have a channel name and a resource ID. So a channel might be named "visitors", where a channel resource might have the channel name "members" and the resource ID "weather".  Although they are named differently a channel resource's use is very similar to that of a channel. You can publish on and subscribe to both channels and channel resources. Note though that in terms of publishing and subscribing a channel resource is not related in any strict sense to a channel of the same name.  To illustrate this point, let's consider what happens if I have a channel and a channel resource. Let's say my channel is named "myChannel". And let's say my channel resource has the same channel name, "myChannel", but also has the resource ID "myResource". The following rules apply when publishing and subscribing: * Messages published on "myChannel" are not published on "myChannel:myResource" and vice versa. * If you subscribe to "myChannel", you will not receive messages from "myChannel:myResource" and vice versa. So why would you use a channel resource if it seems to be the same as a channel? That is answered by the second difference between a channel and a channel resource. When setting a permissions policy, you can set a policy for each individual channel and channel resource, or you can set a policy that applies to a channel and all its resources. If a channel resource does not have its own explicit policy, it inherits the policy of its channel. To understand this point, let's look at the channels we use in our site. We have one channel named "visitors", that we want to use for visitors who are not members of our site. We also have another channel, named "members", that has many channel resources, such as "weather" and "politics". We want to restrict access to our "members" channel resources by defining permissions. We do not need to define specific permissions for each "members" channel resource. Instead, we define one permissions policy for the "members" channel and all its channel resources inherit those permissions. Note that if we want to have a special member channel with even more restrictive permissions we could create a "members" channel resource and override the default "members" permissions or we could choose to create another channel with its own permissions policy.  ### Data Model Now let's take a look at how we store the data needed to make our site work.  First, let's consider the breaking news alerts that we send our site members. We want to have a list of the different types of alerts that members can subscribe to. We also want to be able to easily change this list, adding or removing alert types as necessary. To achieve this we store all the alert information in a collection, named **SubscriptionTypes**, where each item represents an alert type that site members can subscribe to. The items in this collection correspond to the list of alert subscription types that we saw in the subscriptions popup and admin page. Next, because we're creating a site where each member can choose which alert types they want to subscribe to, we need to store each member's subscriptions. To do so we have another collection, called **Subscriptions**, where each item in the collection represents a site member and that member's subscriptions. Since we're going to implement our subscriptions using the Realtime API, our data model reflects the entities of that API, such as channels and channel resources. Each of our subscription types is implemented using a realtime channel resource (more details about this below). So when we store a subscription type we need to store the realtime channel resource's channel name and resource ID. Now let's take a look at the fields in our collections. Notice that we use reference fields to create a relationship between the two collections. #### SubscriptionTypes Collection
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2020/05/12/9d6cdbae-fea7-4d67-ad8d-fdb5190d45f3/40edf690-d0ab-4923-a368-fe8b119c1fc9.png)
* **Type (type)**: The display name of the subscription type. This shows in the subscription popup and admin page. * **Channel Name (channelName)**: Name of the realtime channel for each subscription. (You might notice that all of our channels have the same name, "members". We store this information in case we want to expand our site to use multiple channels in the future.) * **Channel Resource (channelResource)**: Name of the realtime channel resource for each alert. * **Subscriptions (subscriptions)**: A multi-reference field that points to the members in the **Subscriptions** collection who subscribe to each alert. (Here the members show as "Untitled" because of the unused Title field described below.) #### Subscriptions Collection
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2020/05/12/5d524ebc-1343-41c9-b244-95b3356e86dc/7f7944d3-961b-4188-a447-79f65093c8f5.png)
The Subscriptions collection is where each member's selected alert subscriptions are stored. Each item represents a site member. The collection contains the following fields: * **Title (title)**: This field is not used to store data. However, we need to keep it because none of our other fields can serve as the primary field. * **ID (\_id)**: ID of a site member. Note that usually when using collections the ID field is automatically generated. Here we take advantage of the fact that you can assign a specific ID to an item if you create the item using the Wix Data API. So when we create a new member item in this collection we set the ID field to the member's personal ID.   * **Types (types)**: A multi-reference field that points to the subscription types in the **SubscriptionTypes** collection that each member subscribes to. ### Backend Code Let's begin our discussion of the site's code by looking at the code we've placed in the backend. In the backend we've added code for publishing realtime messages. #### realtime.web.js The first backend file we have is a [web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) we've named **realtime.web.js**. We use a web module here because this code needs to be called from the frontend, specifically the admin page. This file contains code for publishing messages on the realtime channels and channel resources that our site members subscribe to. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { publish } from 'wix-realtime-backend'; export const publishMessage = webMethod(Permissions.Anyone, (name, resourceId, message, color) => { const now = new Date(); const channel = {name, resourceId}; const payload = {message, color, time: now.toLocaleTimeString('en-US')}; return publish(channel, payload); }); ``` As you can see, the file contains one function. We call that function from the admin page to publish messages that are received by subscribers on the home page. The function simply takes in some information and packages it up so it can be sent using the Realtime API. When calling the `publish()` function from the Realtime API we need to provide it with where we want to publish and what we want to publish. So we take the `name` and `resourceId` (if there is one) that were passed into the `publishMessage()` function and package it up as a `Channel` object. We also take the information that we want to publish and package it in an object to be sent as the payload. Here we send the textual `message`, the `color` that we want the message to be displayed in, and the `time` the message was sent. #### realtime-permissions.js The second backend file we have is named **realtime-permissions.js**. This is not a web module because this code is not called from the frontend. This is a special file that needs to be named **realtime-permissions.js**. It contains code that implements permissions checks for our realtime channels. Each time a site visitor attempts to subscribe to one of our channels the Realtime API calls the functions in this file to see if the visitor has the permissions required to subscribe to the requested channel. ```javascript import { permissionsRouter } from 'wix-realtime-backend'; permissionsRouter.default((channel, subscriber) => { return { 'read': true }; }); const membersChannel = {'name': 'members'}; permissionsRouter.add(membersChannel, (channel, subscriber) => { if (subscriber.type === 'Member' || subscriber.type === 'Admin') { return { 'read': true }; } else { return { 'read': false }; } }); export function realtime_check_permission(channel, subscriber) { return permissionsRouter.check(channel, subscriber); } ``` In our code, we've elected to use the permissions router, which allows you to create permissions policies in an organized manner. Let's analyze this code one part at a time. * * * First, we use the `default()` function to set the default permissions for all channels and channel resources. In our case, we set the default permissions to allow anyone to read. We do so by returning the permissions policy from the callback function passed when calling `default()`. ```javascript permissionsRouter.default((channel, subscriber) => { return { 'read': true }; }); ``` In our site, we have a "visitors" channel that we subscribe non-members to. Since we don't specify specific permissions for that channel, it receives the default permissions and anyone can subscribe to it. * * * Next, we create a `Channel` object to represent our "member" channel and use the `add()` function to add permissions for it. Since we don't specify specific permissions for each resource in the "members" channel, all the resources inherit the permissions we define here. ```javascript const membersChannel = {'name': 'members'}; permissionsRouter.add(membersChannel, (channel, subscriber) => { if (subscriber.type === 'Member' || subscriber.type === 'Admin') { return { 'read': true }; } else { return { 'read': false }; } }); ``` Here again, we specify the permissions by returning them from a callback function. In this case, we check if the visitor trying to subscribe to a "member" channel is a site member or the site admin. If so, we grant them read permissions. If not, we deny them read permissions. * * * Finally, we define the `realtime_check_permission()` function. This is the function that gets called each time someone tries subscribing to a channel or channel resource. It gets passed which channel someone is trying to subscribe to and who it is that is trying to subscribe. It returns the permissions that are granted to that subscriber for that channel. For example, when visitor "MsVisitor" tries to subscribe to channel "SomeChannel" the `realtime_check_permission()` function is called. The `channel` argument contains a `Channel` object corresponding to "SomeChannel" and the `subscriber` argument contains an object corresponding to "MsVisitor". In the implementation of this function you can take into account who the visitor is and what channel they are trying to subscribe to. Then you return a `ChannelPermissions` object that defines what permissions you've granted "MsVisitor" on "SomeChannel". Technically, this is the only function we need to implement in order to define realtime permissions. We could have crammed all of our permissions logic into the `realtime_check_permission()` function. Instead, we've used the permissions router, which leads to neater code. ```javascript export function realtime_check_permission(channel, subscriber) { return permissionsRouter.check(channel, subscriber); } ``` At this point, since we used the permissions router to define our permissions policies, all we need to do is have the permissions router check the permissions policy for the current subscriber on the requested channel and return the result. The permissions router will use the rules we defined above to determine which permissions to grant. * * * Now we can look at our page code and see where the backend functionality we've just discussed is used. ### Admin Page Code We've already laid much of the groundwork needed to implement our admin page, but there is still a little work to do in the page code itself. Basically, we need to add code to retrieve and display the types of alerts that an admin can publish and actually publish the alerts. ```javascript import wixData from 'wix-data'; import {publishMessage} from 'backend/realtime.web'; $w.onReady(async function () { let channels = await wixData.query('subscriptionTypes').find(); channels.items.unshift({_id: 'visitors', channelName: 'visitors', type: 'Visitors'}); let options = channels.items.map(channel => ({label: channel.type, value: channel._id})); $w('#subscriptions').options = options; $w('#sendButton').onClick( () => { $w('#sending').show(); $w('#sendButton').disable(); let promises = $w('#subscriptions').value.map( (subscription) => { let selectedChannel = channels.items.find(channel => channel._id === subscription); return publishMessage( selectedChannel.channelName, selectedChannel.channelResource, $w('#message').value, $w('#colors').value ); } ); Promise.all(promises) .then( () => { $w('#sendButton').enable(); $w('#sending').hide('fade'); } ); } ); }); ``` Once again, let's analyze this code one part at a time. * * * ```javascript $w.onReady(async function () { let channels = await wixData.query('subscriptionTypes').find(); channels.items.unshift({_id: 'visitors', channelName: 'visitors', type: 'Visitors'}); let options = channels.items.map(channel => ({label: channel.type, value: channel._id})); $w('#subscriptions').options = options; ``` When the page loads, we start by populating the checkbox group that the admin uses to select which channels to publish on. Most of the channel data comes from a query to the **SubscriptionTypes** collection. However, we also add the option of the "visitors" channel to the list. Once we have our list, we transform each channel item into a checkbox group option object and populate the checkbox group. * * * We also define what should be done when the send button is clicked. ```javascript $w('#sendButton').onClick( () => { $w('#sending').show(); $w('#sendButton').disable(); ``` First, we show a message to notify the admin that the send is in progress and we disable the send button so the admin doesn't try sending again before the current send is finished. * * * ```javascript let promises = $w('#subscriptions').value.map( (subscription) => { let selectedChannel = channels.items.find(channel => channel._id === subscription); return publishMessage( selectedChannel.channelName, selectedChannel.channelResource, $w('#message').value, $w('#colors').value ); } ); ``` Then we get all the channels that were selected by the admin from the checkbox group's `value` property. For each selected channel, we find the channel's name and resource ID and use it to publish using the function we defined in the backend **realtime.web.js** file. In addition to the channel information, we also pass the `publishMessage()` function the message entered by the admin and the color the admin chose. * * * ```javascript Promise.all(promises) .then( () => { $w('#sendButton').enable(); $w('#sending').hide('fade'); } ); } ); }); ``` Finally, we wait for all the calls to publish messages to resolve so we can enable the send button and hide the sending notification. Now the admin can publish another message. The messages published on this page are received by site visitors on the home page who are subscribed to the same channels. ### Home Page Code On the home page, we need to write code to deal with visitors logging in to the site, subscribing and unsubscribing visitors to and from channels, and handling alerts when they are received. As usual, let's take a look at the code for this page one piece at a time. #### onReady( ) ```javascript import wixData from 'wix-data'; import { authentication, currentMember } from 'wix-members-frontend'; import { openLightbox } from 'wix-window-frontend'; import { subscribe, unsubscribe } from 'wix-realtime-frontend'; $w.onReady(async function () { if (authentication.loggedIn()) { let member = await currentMember.getMember(); intializeMember(member._id); } else { subscribeToVisitorChannel(); } authentication.onLogin(async (member) => { intializeMember(member.id); unsubscribe({channel: {name: 'visitors'}}); }); }); ``` After the necessary imports, our code defines what to do when the page loads. First we check to see if the current visitor is logged in. If so, we call a function to initialize the page for the member experience. We'll take a look at the details of what that entails below. If the current visitor is not logged in, we call a function to subscribe the visitor to the "visitors" channel. We also define what happens when a visitor who was not logged in logs in. Again, we call a function to initialize the page for the member experience. We also unsubscribe the member from the "visitors" channel. Before we take a deep dive into the `intializeMember()` function, let's take a look at the simpler `subscribeToVisitorChannel()` function. #### subscribeToVisitorChannel( ) This function is used to subscribe visitors to the "visitors" channel. ```javascript function subscribeToVisitorChannel() { const visitorChannel = { name: 'visitors' }; return subscribe(visitorChannel, showBreakingNews); } ``` First, we create a `Channel` object to represent the "visitors" channel. Notice that we only use a `name` and not a `resourceId` because we don't have multiple visitor alert types. Then we call the `subscribe()` function from the Realtime API. The `subscribe()` function takes two arguments. The first is a `Channel` object and the second is a callback function to call each time a message has been published on that channel. So in this call to the `subscribe()` function, each time a message is published to the "visitors" channel we want to call the `showBreakingNews()` function to handle the incoming message. Now let's take a look at how the `showBreakingNews()` function works. #### showBreakingNews( ) This function is used to display the breaking news alerts that have been received from channels. ```javascript function showBreakingNews({ payload }) { $w('#breakingText').html = `
(${payload.time}) ${payload.message}
`; $w('#breakingStrip').show("fade"); } ``` First, we take the received payload and format it in the style we want for our breaking news alert. Remember from our discussion of the backend code that the payload contains a `message`, `color`, and `time`. Here we use the `html` property of a text element so we can change the color of the alert using the `style` attribute and populate the `time` and `message` as stylized text. Then, all we have to do is show the strip containing our text element. Now that we've seen how we handle visitors who are not logged in, let's see how we handle members that are logged in. #### intializeMember( ) This function is used to set up the page for members who are logged in and to subscribe them to all the alerts that they've set. ```javascript async function intializeMember(memberId){ let subscriptions = []; $w('#settings').show(); const initialSubscriptions = await getSubscriptions(memberId); subscribeToMemberChannels(initialSubscriptions, subscriptions); $w('#settings').onClick(() => { openLightbox('Subscriptions', subscriptions) .then(({ added, removed }) => { subscribeToMemberChannels(added, subscriptions); unsubscribeFromChannels(removed, subscriptions); }); }); } ``` First, we create an array named `subscriptions`, that will hold the member's subscriptions. We use this array to keep the current state of the member's subscriptions. Each time the member subscribes to or unsubscribes from a channel we update this array. Next, we show the settings icon using the `show()` function. Remember, that is how site members open the subscription popup to set which alerts they want to subscribe to. Then, we get the list of channels the member has subscribed to using the `getSubscriptions()` function. Remember, these are stored in the **Subscriptions** collection. Once we get the list of channels we call the `subscribeToMemberChannels()` function to subscribe the member to each of those channels. Finally, we define what happens when the settings icon is clicked using the `onClick()` function. When it's clicked we open the subscriptions popup. We also define here what happens when the popup is closed. In our case, when the popup closes it returns to the page a list of channels that the member added and a list of channels that the member removed. So we call a couple of methods to subscribe the member to the added channels and unsubscribe the member from the removed channels. Before taking a look at the functions we use to do the subscribing and unsubscribing, let see how we get the channels a member had subscribed to. #### getSubscriptions( ) This function is used to get the subscriptions that a member was subscribed to the last time they visited the site. ```javascript function getSubscriptions(memberId) { return wixData.queryReferenced('subscriptions', memberId, 'types') .then(({ result: { items } }) => items) .catch(err => { wixData.insert('subscriptions', { _id: memberId }); return []; }); } ``` Remember, the **Subscriptions** collection has a field name `types` that is a reference to all the subscription types a member is subscribed to. Here, we use the `queryReferenced()` function to get those referenced items based on the current member's ID. If the function runs successfully, we know we are dealing with a member that we have already added to our collection. However, if the function's returned promise is rejected, we are assuming that the rejection is caused by the current member being a new member without a corresponding item in the **Subscriptions** collection. In that case, we insert a new item into the collection where the item's `_id` is the current member's ID and return an empty array because a new member does not have any saved subscriptions.
**Important:** The function's promise may have been rejected for other reasons, such as connectivity issues. In the interest of simplicity, we don't deal with that possibility in this example.
#### subscribeToMemberChannels( ) This function is used to create a subscription for all the channels a member has saved in the subscriptions popup. ```javascript function subscribeToMemberChannels(channels, subscriptions) { channels.forEach(channel => { let memberChannel = { name: channel.channelName, resourceId: channel.channelResource }; subscribe(memberChannel, showBreakingNews) .then(subscriptionId => { subscriptions.push(channel); }); }); } ``` For each channel in the list passed to the function, we create a `Channel` object and call the realtime `subscribe()` function to subscribe the member to the channel. Just as we did above, we pass the `showBreakingNews()` function as the callback function to be called when a message is received on the channel. If the subscription is successful, we store the channel information in the `subscriptions` array. #### unsubscribeFromChannels( ) This function is used to unsubscribe a member from channels. It is called after a member deselects a subscription in the subscriptions popup. ```javascript function unsubscribeFromChannels(removed, subscriptions) { removed.forEach(id => { let toRemove = subscriptions.find(subscription => subscription._id === id); let toRemoveIndex = subscriptions.findIndex(subscription => subscription._id === id); unsubscribe({ channel: { name: toRemove.channelName, resourceId: toRemove.channelResource } }) .then(() => { subscriptions.splice(toRemoveIndex, 1); }); }); } ``` For each ID of a removed subscription, we find the corresponding object in the `subscriptions` array and the index of where it resides in the array. We use the object data to call the realtime `unsubscribe()` function. If the unsubscribe is successful, we use the index to remove the items from the `subscriptions` array. ### Subscriptions Popup Code In the subscriptions popup, we need to write code that allows members to choose which alerts they want to subscribe to and unsubscribe from. As we saw in the home page code, the actual subscribing and unsubscribing happens there. Here, in the subscriptions popup, we just have to collect the information and pass it to the home page.  As always, we'll analyze the code one part at a time. ```javascript import wixData from 'wix-data'; import { currentMember } from 'wix-members-frontend'; import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(async function () { let member = currentMember.getMember(); let memberId = member._id; let selectedIndices = []; let startValues = []; let channels = await wixData.query('subscriptionTypes').find(); let options = channels.items.map((channel) => { return { label: channel.type, value: channel._id } }); $w('#subscriptions').options = options; // onReady() continues below... ``` After the necessary imports and declaration of some variables, the first section of code that runs when the popup opens populates the checkbox group in the popup with all the possible subscription types. We've already seen this done on the admin page, and the code here is very similar. 
**Tip:** For performance improvements, try retrieving the subscription types the first time the popup opens and then store that information using the wix-storage-frontend API for any subsequent times it opens.
* * * ```javascript // ...onReady() continued from above let subscriptionIds = wixWindowFrontend.lightbox.getContext().map((subscription) => subscription._id); options.forEach((option, index) => { if (subscriptionIds.includes(option.value)) { selectedIndices.push(index); startValues.push(option.value); } }); $w('#subscriptions').selectedIndices = selectedIndices; // onReady() continues below... ``` The next section of code that runs when the popup opens selects all the options in the checkbox group that the current member has subscribed to. The code first gets the IDs of all the subscriptions passed from the home page when opening the popup. Then, for each option in the checkbox group, it checks to see if it exists in the list of subscription IDs. If so, it sets the option to selected. The code also stores these starting subscriptions in a list. This will be used later when determining if the member selected any new subscriptions. We'll reference our ending subscription list against this starting list.  * * * ```javascript // ...onReady() continued from above $w('#save').onClick(() => { let endValues = $w('#subscriptions').value; let added = endValues.filter(x => !startValues.includes(x)).map(addedId => { return channels.items.find(channel => channel._id === addedId); }); let removed = startValues.filter(x => !endValues.includes(x)); wixData.replaceReferences('subscriptions', 'types', memberId, $w('#subscriptions').value); wixWindowFrontend.lightbox.close({added, removed}); }); // end of onReady() ``` The final section of code that runs when the popup opens defines what happens when the save button is clicked. When it is clicked, we get the list of subscriptions that the member selected. We cross-reference this list against the starting list to determine which subscriptions the member added and which ones the member removed. We update the member's entry in the **Subscriptions** table to reflect the new subscription statuses and send the list of added and removed subscriptions back to the home page to be handled there. ### Learn More To learn more about the Realtime API see the [wix-realtime-frontend](https://www.wix.com/velo/reference/wix-realtime.html) and [wix-realtime-backend](https://www.wix.com/velo/reference/wix-realtime-backend.html) sections of the API Reference. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using SAML SSO with Velo ## Article: Using SAML SSO with Velo ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-saml-sso-with-velo.md ## Article Content: # Using SAML SSO with Velo Single Sign On (SSO) is a great way to make life easier for your site visitors and encourage them to register as a user on your site. Visitors can log in to your site using their Google, Facebook, or other accounts, or using their work account. This article describes how to implement SAML, Security Assertion Markup Language, an open standard that allows identity providers (IDP) to pass authorization credentials to service providers (SP). To learn about implementing SSO with oAuth, see [Using OAuth SSO with Velo](https://support.wix.com/en/article/using-oauth-sso-with-Velo). In our example, the service provider is our Wix site. We will also need an Identity Provider. The IDP authenticates our visitor and passes the visitor's credentials along to our site. For this tutorial we are going to use [Auth0](https://auth0.com/) as our IDP. Like many other services, Auth0 offers a free trial to help you get going. Note that while the SAML protocols are standard, each IDP has their subtle differences, so if you use a different IDP, you may need to tweak some of the settings in the functions used here. ## The SAML Authentication Sequence The sequence diagram below shows the basic flow between the browser, our Wix site (the SP), and the IDP. 1. The user clicks a sign-in button. 2. The SP generates a SAML request using the IDP and SP metadata, and passes the request and URL to the browser. 3. The browser sends the SAML request to the IDP. 4. The IDP presents a login page, based on the SAML request. 5. The user provides login credentials. 6. The IDP posts the assertion to the SP. A SAML assertion is an XML document that contains the user authorization and user credentials. 7. The SP decrypts, validates, and extracts the credentials from the assertion. 8. The SP (Wix backend) generates a session token, and passes a redirect back to the browser with the session token in the query parameters. 9. The browser redirects to the front-end page. 10. The session token is used to sign in automatically.
## Setting Up There are a number of tasks to accomplish before starting to code: 1. Set the name of the callback function that handles the assertion 2. Create an SSL certificate and private key pair. 3. Set up the IDP. 4. Download the IDP metadata. 5. Set up the SP metadata. ### Naming the Assertion Callback Function This function handles the POST request from the IDP containing the encrypted user credentials. The function is defined in the [**http-functions.js**](https://www.wix.com/velo/reference/wix-http-functions/introduction) back end file. Our function needs to handle the post of the Assertion Consumer Service (ACS) URL so let's call it **post\_assertion.** The ACS is an XML document that contains the user authorization and user credentials. If our site's URL is `https://mysite.wixsite.com/saml-demo,` then the URL of our assertion handler is `https://mysite.wixsite.com/saml-demo/\_functions/assertion` ### Creating Certificates and Keys We'll be encrypting and decrypting requests and responses between the SP and IDP. To do this we'll need an X509 private key and certificate. To generate a private key and certificate, go to your system prompt and enter the following: ``` openssl genrsa -out private.pem openssl req -new -x509 -key private.pem -out public.crt ``` You will be asked a number of questions. It is enough to answer the first one with a two-letter country code. This will produce two files, **private.pem** containing the private key and **public.crt** containing the public certificate. These files will be created in the directory where you ran the commands. Remember where you put **public.crt** as we'll need it later. >**Note** > If you are using Windows, you may need to install OpenSSL before running these commands ### Using the Secrets Manager We'll use the [Wix Secrets Manager](https://support.wix.com/en/article/Velo-about-the-secrets-manager) to securely store our private key secret. This is more secure than pasting the key into our backend code. Make sure never to expose your private key. In the Secrets Manager, set the **Name** of the secret to **spPrivateKey.** Use a text editor to copy and paste the entire contents of **private.pem** including the **\-----BEGIN RSA PRIVATE KEY-----** and **\-----END RSA PRIVATE KEY-----** lines into the **Value** field. Enter a description so that you remember what this secret is used for.
If you haven't used the Secrets Manager before, here's how to use it. 1. Go to your Wix Dashboard. 2. Select **Developer Tools** and then **Secrets Manager.** 3. Click the **Store Secret** button and enter the following details: ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/10/18/580ac3a4-b836-4108-bda1-6e3e7be2383b/555733bb-d5e1-4f24-b20f-d7bed6f76179.png) 4. **Name:** The name of the secret to be used in the code. 5. **Value:** Paste the text value of the secret. 6. Enter a brief description to remind you what this secret is used for, and click **Save**.
### Setting Up the IDP We need to configure IDP and export some metadata. This metadata will be used in our code when creating the SAML request, and when we want to decrypt the response and extract the credentials. Set up a trial account. For this tutorial, we used Auth0. Sign up [here](https://auth0.com/signup?&signUpData=%7B%22category%22%3A%22button%22%7D&email=undefined) for their free trial. Once you have set up your trial account, follow these steps to create an application with SAML2 capabilities. 1. Click **Applications** in the Auth0 dashboard. 2. Click **Create Application,** give your application a name, select **Regular Web Applications and** click **Create**. 3. Click **Settings** in the top bar. 4. Scroll down to **Allowed Callback URLs** and enter the URL of your assertion function. This is the URL that the IDP will call with a POST when the user signs in. In our case that's going to be `https://mysite.wixsite.com/saml-demo/\_functions/assertion` which corresponds to the function **post\_assertion** in the **http-functions.js** file. More on that when we get to the code. 5. Scroll to the bottom and click **Save Changes**. 6. Scroll back up to the top bar and click **Addons**. 7. Click **SAML2 WEB APP**. 8. The IDP needs our public certificate and Auth0 requires that it fits on a single line. The public key was that file we stored as **public.crt** earlier on. In the settings box, add **"signingCert": ** inside the curly braces.The format for the public key is: `-----BEGIN PUBLIC KEY-----nMIGf...bpP/t3\n+JGNGIRMj1hF1rnb6QIDAQAB\n-----END PUBLIC KEY-----\n`so you will need to reformat your certificate to change all newline characters (\\n) to actual "\\"s followed by "n"s. Use the following if you are on mac or linux: `$ awk '{printf "%s\\n", $0}' public.crt ` Use the following command in PowerShell if you are on Windows: `PS C:\Users\Wix> (Get-Content .\public.crt) -join "\n"` It should look like this:
9. Scroll all the way to the bottom and hit **Enable,** then **Save**. 10. Scroll up to the top of the window and click **Usage** in the top bar**.** 11. In the **Identity Provider Metadata** section, click the **Download** link and save the file. 12. Ccreate a backend file called **metadata.js**. 13. Copy the XML from the downloaded file into **metadata.js** and export it as the constant **idpMetadata.** Use backticks ( \` ) to enclose the XML so that the end of line is ignored. 14. Edit the **IDPSSODescriptor** and add **WantAuthnRequestsSigned="true"** as in Line 2 below. Your code should look like this: ```javascript export const idpMetadata =` MIIDDTCCAfWgAwIBAgIJGBvK29ZE/GY1MA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNVBAMTGW15LXNhbWwtdGVzdC51cy5hdXRoMC5jb20wHhcNMjAxMDAxMDk0MjIyWhcNMzQwNjEwMDk0MjIyWjAkMSIwIAYDVQQDExlteS1zYW1sLXRlc3QudXMuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4yxmeg9zZn/Q1TzI27hQIknZaSklARUAlEtayykvrC9glB7SBuo1b0rz0w+M1GAKzdgcM3Ca0a0iC/wfH9a0YbXXE1vwj5tlMqISEKEPv/SOep1glPrK7m3s8ndPsfVN/4cxQbncVkLSgwORylWaU1UMU8Im3yvL7WcL0uSKCUQWr/XEbQsNqn+dDIjwXCAC2smXho7nm5Alz2xwR1bORLCLPTcJuy2pr0LxuzZiw366gADy/mG21OBOnPYqTbPZJuRE1I46av/atJVEPFrn3hklsyVaS4M3YEl7x+nW0rzenvqQvwh56ZDvF2cNUQBMqjtblclJd4ipp3x0bLuziQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQzlOf36gQB747obsUfV8wb6jLVAjAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAMs2wZW/CQtZFHS7tltolBNMDTuPokrRUlZYT6KoKdTh0Uk4Okq0luCEwAoxbyidpBMEWUIKP74iEttK72QiUF8Wf93DZtd0pC/IgMlFR+ef5z7IEunFTUJ0DsutdQh6BDPZKqoo/gG3eefBeCt0lYav/aF0v2+vzHZUc9wWGYuuKs8wjp4a+hxll3/lgzX/jGvvl69ckJ1TW71LtUF42RRtZcvbj5eC1xxC9jrF6hsLrdwQpaiVB0obyp260iXetc1cSuyVVThGQL3XIq4H7ngLGikJk53lESOJknn0DoTzg1mVnKZ6t47WcWU3eOQomgUVyxszkrwEpEmEXKEc+r8= urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient ` ``` ### Setting up the SP Metadata Now that we have our private key and certificate we can set up our service provider metadata. This metadata will be used to create and encrypt the SAML requests. To create the metadata we'll use a handy util at [https://www.samltool.com/sp\_metadata.php](https://www.samltool.com/sp_metadata.php). You should be on the **Build SP Metadata** page. 1. Chose an EntityId, any unique name is OK. Let's use **saml-demo**. 2. **Attribute Consume Service Endpoint (HTTP-POST)**: This is where you enter the Assertion Consumer Service endpoint that will handle the assertion. In our case, as we defined above, it's `https://mysite.wixsite.com/saml-demo/\_functions/assertion`. 3. **SP X.509 cert:** Open the **public.crt** file with a text editor. This is the file we created in the [**Certificates and Keys**](https://support.wix.com/en/article/using-saml-sso-with-Velo#creating-certificates-and-keys) step. Copy and paste the file's content into the **SP X.509 cert** field. 4. Set **AuthnRequestsSigned** and **WantAssertionsSigned** to **True.** 5. Scroll down and hit **BUILD SP METADATA.** 6. Copy the XML from the green **SP Metadata** box. 7. Open the backend file called **metadata.js** that you created for the IPD metadata and paste the copied XML into the file. Export it as **spMetadata.** Use back-ticks ( \` ) to enclose the XML so that the end of line is ignored.Your code should look like this : ```javascript export const spMetadata = ` MIIE6DCCAtACCQCgAlaiRpc7ITANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJJTDELMAkGA1UECAwCaWwxDTALBgNVBAcMBEhlcmUxCzAJBgNVBAoMAk1lMB4XDTIwMTAwNzA4NDc1N1oXDTIwMTEwNjA4NDc1N1owNjELMAkGA1UEBhMCSUwxCzAJBgNVBAgMAmlsMQ0wCwYDVQQHDARIZXJlMQswCQYDVQQKDAJNZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM1JcEsUkUWsQhqn5E61MEjzkyOJQoF91WrQTsKhNsA0KiRoVcZ4OkGUUFYWrSCZXxab3VrghBURhW9UhJE5zho9rXX1PHvqnzEtS+/hzoQgBFZG3Halo/U06HeVXD4n2uJTlb/K/1KHXbUsJ3IniIA+IRhmlP69Oi/ZVQvjkfLDBbillp4vMPcf/ReT0/IGG3zRPX61byLmVoWLxzuQlqhojilRgfdn+gvLpDgleMaw9FR0EjkolTj6VW6A7nlw5MTKaMDATUGRnfUP+pbnq4fluXdfVzcNHF3JFQ8ECvePm1XqAr9hwIk6UX50gFjUM1A+hSDt298V55iG6RugwLaoO83p5tVd3g0xkxamPRAKxxSZfNia3y4HJB8cGTVor4SnwvkL8mweIr/xmDayyFzLYl5Tux7uk/nwzFN80EG9MieEjFgTMKgXzi3TGnyJQspypM8dqyN6eUpn0N/rKB5jaeMgXnUqRhrKc0aQtMUkhqAzTMMWFnbDySAKHJhq+HI4SXFEXhf51GM2tkoEh8OBhwbcoB8tco60I7cBxT0kakqoFWHG5jCTd4ksGzUkXoTeYrQSyxE/DyCOfZBzkvljJLcqoOQwoukYV13WLOfsNgETIiAhe0PXhqi0B8kxn6SXVqhVaVKhsXiy4FbClLOefPFHH+wtViNYjWQUIVctAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAM0XAs4qZGwYN34YxqYThVHGDcWljO9DiKlCt3xbBBRX9ZI8VKOWHNtswdBbdraPtAMT0D052xSWMbXOGlnGWPHxjvLCsWfGDnxJ4KCyItkzKuoIm7LRmSpQWHDwZNTKlQVm9ap192vvOgLa53mHavOZJ7NPoVkO29MC7+NyUa8exn9Zj8XbRGr2pioYK4LMub4oFthuAHVg5RIzHmIMzJpZYew8OrSYXLHLDKLWNLQHDOwDl+Bic02IQIF/hCfflviGGLCk3yjiAVP4HnpkJgtdDkgMi+YihG8znlyHpSmB60WrlSjbbxgxkdmPePEhZP5BrYeSyV6ZM2I5dpj3GpG94JN5RC44BpL23IXlS89dzRfo/cKKIZ63uA45rH6vVNEqAnKfSFGRI0OuafEdx8Cirgkw7WXMHkzqEm5wYlt4XV5akavi0PErS9rrYGlMpfUxT2csyjCww6k/UaxBDaS9GAySAMwRPsuA853XY0/eSESWNj72i0bGCjT1Dx6MWaE/UVERTSL04qxK/ACJT9aMnOAjs2r1gFYZq57l1AC5eadXtcJt/s6C/uEXgvXhyPvvHnYisu7S2lu5rOg1N9cvVftWbeRgTgAEsr3cz04CZ4ZZRADhpVpE38Shq2vPNrf4qqkjbM1BgOGcXtx/lDVUUlM4l7FEjWNfrUWbueqh MIIE6DCCAtACCQCgAlaiRpc7ITANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJJTDELMAkGA1UECAwCaWwxDTALBgNVBAcMBEhlcmUxCzAJBgNVBAoMAk1lMB4XDTIwMTAwNzA4NDc1N1oXDTIwMTEwNjA4NDc1N1owNjELMAkGA1UEBhMCSUwxCzAJBgNVBAgMAmlsMQ0wCwYDVQQHDARIZXJlMQswCQYDVQQKDAJNZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM1JcEsUkUWsQhqn5E61MEjzkyOJQoF91WrQTsKhNsA0KiRoVcZ4OkGUUFYWrSCZXxab3VrghBURhW9UhJE5zho9rXX1PHvqnzEtS+/hzoQgBFZG3Halo/U06HeVXD4n2uJTlb/K/1KHXbUsJ3IniIA+IRhmlP69Oi/ZVQvjkfLDBbillp4vMPcf/ReT0/IGG3zRPX61byLmVoWLxzuQlqhojilRgfdn+gvLpDgleMaw9FR0EjkolTj6VW6A7nlw5MTKaMDATUGRnfUP+pbnq4fluXdfVzcNHF3JFQ8ECvePm1XqAr9hwIk6UX50gFjUM1A+hSDt298V55iG6RugwLaoO83p5tVd3g0xkxamPRAKxxSZfNia3y4HJB8cGTVor4SnwvkL8mweIr/xmDayyFzLYl5Tux7uk/nwzFN80EG9MieEjFgTMKgXzi3TGnyJQspypM8dqyN6eUpn0N/rKB5jaeMgXnUqRhrKc0aQtMUkhqAzTMMWFnbDySAKHJhq+HI4SXFEXhf51GM2tkoEh8OBhwbcoB8tco60I7cBxT0kakqoFWHG5jCTd4ksGzUkXoTeYrQSyxE/DyCOfZBzkvljJLcqoOQwoukYV13WLOfsNgETIiAhe0PXhqi0B8kxn6SXVqhVaVKhsXiy4FbClLOefPFHH+wtViNYjWQUIVctAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAM0XAs4qZGwYN34YxqYThVHGDcWljO9DiKlCt3xbBBRX9ZI8VKOWHNtswdBbdraPtAMT0D052xSWMbXOGlnGWPHxjvLCsWfGDnxJ4KCyItkzKuoIm7LRmSpQWHDwZNTKlQVm9ap192vvOgLa53mHavOZJ7NPoVkO29MC7+NyUa8exn9Zj8XbRGr2pioYK4LMub4oFthuAHVg5RIzHmIMzJpZYew8OrSYXLHLDKLWNLQHDOwDl+Bic02IQIF/hCfflviGGLCk3yjiAVP4HnpkJgtdDkgMi+YihG8znlyHpSmB60WrlSjbbxgxkdmPePEhZP5BrYeSyV6ZM2I5dpj3GpG94JN5RC44BpL23IXlS89dzRfo/cKKIZ63uA45rH6vVNEqAnKfSFGRI0OuafEdx8Cirgkw7WXMHkzqEm5wYlt4XV5akavi0PErS9rrYGlMpfUxT2csyjCww6k/UaxBDaS9GAySAMwRPsuA853XY0/eSESWNj72i0bGCjT1Dx6MWaE/UVERTSL04qxK/ACJT9aMnOAjs2r1gFYZq57l1AC5eadXtcJt/s6C/uEXgvXhyPvvHnYisu7S2lu5rOg1N9cvVftWbeRgTgAEsr3cz04CZ4ZZRADhpVpE38Shq2vPNrf4qqkjbM1BgOGcXtx/lDVUUlM4l7FEjWNfrUWbueqh urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified ` ``` In the metadata, check the following items: * **SPSSODescriptor** Line 3. Make sure that **AuthnRequestsSigned="true" WantAssertionsSigned="true"** are both there. If not, or if they are false, it may cause trouble later on when creating the ServiceProvider and IdentityProvider objects. * **AssertionConsumerService Binding**, Line 19. This holds the URL that will handle the callback once our visitor has signed in. i.e. `https://mysite.wixsite.com/saml-demo/\_functions/assertion` ### Installing the Samlify Package Before we start coding we need to install the [**samlify**](https://samlify.js.org/#/) package so we can call its functions from our code. To install a package: 1. In the left sidebar of the editor, navigate to **Packages & Apps**. In Wix Studio, you can find this in the ![Code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/43681438c0ad6af225fb8c944cff74b4.png "Code") tab. 2. Under **npm** click **Install packages from npm.** 3. Search for "samlify" and click **Install** next to the **samlify** package. ## Adding the Code Add the following code files to your backend files: * **saml.web.js:** Accessible from the front end, to generate SAML requests. * **create-providers.js:** To work with the samlify package to create the SP and IDP objects. This is a **.js** file and therefore not accessible from the front end. We do not want these functions accessible from the front end as they contain metadata and secrets that we do not want to expose. * **http-functions.js:** To handle the assertion callback from the IDP, and retrieve the user's details.
**Note** You must use the file name "http-functions.js" to handle the web callbacks but you can call the other files any name you like.
### Adding Landing Page Code Add a login button to your landing page. Then add the following code: ```javascript import wixLocationFrontend from 'wix-location-frontend'; import {session} from 'wix-storage-frontend'; import { generateSAMLRequest } from 'backend/saml.web'; $w.onReady(function () { $w('#loginButton').onClick(() => { generateSAMLRequest() .then((request) => { session.setItem("requestId", request.id); wixLocationFrontend.to(request.context); }); }); }); ``` #### Understanding the Code **Lines 1-2:** Import the modules that we will work with, including our backend function, `generateSAMLRequest`, which we will create below. **Lines 7-8:** When a site visitor clicks login the button, we call the backend `generateSAMLRequest` function, which will return the SAML request Id, and the URL with the SAML request. **Line 10:** Store the `requestId` in the session variables. The `requestId` will be compared to the `inResponseTo` Id that is returned in the SAML response. This will help us protect against someone copying the final log-in URL and the session token. **Line 11:** Direct the browser to the request URL with `wixLocationFrontend.to`, so the visitor can log in using their credentials on the IDP. ### Generating the SAML Request Create the sp and idp objects, using their respective metadata, and use these objects to create SAML request. Return the SAML request URL to front end. The **generateSAMLRequest** function in **saml.web.js**, returns a SAML request URL to the front end, which calls [`wixLocationFrontend.to()`](https://dev.wix.com/docs/velo/api-reference/wix-location-frontend/to.md) to direct the browser to that URL. At this point the visitor sees the IDP's sign-in window and enters their user and password. Once the IDP authenticates the visitor, it redirects back to our site, to the URL defined in the **AssertionConsumerService Binding** element in the SP metadata. This is the same URL that we set up in the IDP's console. In our example, this will redirect to the **post\_assertion** function, which is defined in **http-functions.js**. Add the following code to saml.web.js. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { notFound } from 'wix-http-functions'; import { createIdp, createSp } from 'backend/create-providers'; export const generateSAMLRequest = webMethod(Permissions.Anyone, async () => { try { //create the idp and sp objects using samlify const idp = await createIdp(); const sp = await createSp(); //create the request url and return it and the request id to the fronted page. const { id, context } = sp.createLoginRequest(idp, 'redirect') return { id, context }; } catch (error) { console.error('generateSAMLRequest error:', error.message); return notFound({ status: 404 }); } }); ``` #### Understanding the Code **Lines 1-3:** Import the modules that we will work with. **Lines 8-9:** When the front end calls `generateSAMLRequest`, we create the identity provider and service provider objects using `createIdp` and `createSp`. **Lines 12-13:** Use the samlify function, `createLoginRequest` to generate the SAML request id and URL and return them to the front end. The request id will be matched with the `inResponseTo` id in the SAML response. Create the **idp** object using the metadata in **idpMetadata**. Add the following code to create-providers.js. ```javascript import { getSecret } from 'wix-secrets-backend'; import { IdentityProvider, ServiceProvider } from 'samlify'; import { spMetadata, idpMetadata } from 'backend/metadata.js'; export function createIdp() { //create the IdentityProvider object using the metadata in idpMetadata try { const idp = IdentityProvider({ metadata: idpMetadata }); return idp; } catch (error) { console.error('Error in createIdp detected', error.message); } } ``` #### Understanding the Code **Lines 1-3:** Import the modules that we will work with. Note the import of `spMetadata` and `idpMetadata` from `backend/metadata.js`. We created the `metadata.js` file when [setting up the IDP](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-saml-sso-with-velo.md) and [setting up the SP metadata](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-saml-sso-with-velo.md). **Line 8:** Use the samlify `IdentityProvider` object and the metadata from the identity provider to create the `idp` object. This object is used when creating the login request and again when we parse the login response in the `http-functions`. **Line 11:** Return the `idp` object to the `generateSAMLRequest` function The **createSp** function retrieves the private key from the Secrets Manager, and uses it with our service provider metadata to create the **sp** object. Add the following code to create-providers.js. ```javascript export async function createSp() { try { // retrieve the private key from the Secrets Manager const spPrivateKey = await getSecret('spPrivateKey'); //create the ServiceProvider object using the metadata and the private key const sp = ServiceProvider({ metadata: spMetadata, privateKey: spPrivateKey }); return sp; } catch (error) { console.error('Error in createSp detected', error.message); } } ``` #### Understanding the Code **Line 5:** Retrieve the private key that was stored in the Secrets Manager in the [Using the Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-saml-sso-with-velo.md) section. **Lines 8-10:** Use the samlify `ServiceProvider` object, metadata for the service provider, and the private key, to create the `sp` object. **Line 12:** Return the `sp` object to the `generateSAMLRequest` function.
**Note** If there are any mismatches between the IDP and SP metadata, it may fail at this point. The most common error is `**ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG,**` and is caused by not setting **WantAuthnRequestsSigned="true"** in the IDP metadata when the SP metadata has**AuthnRequestsSigned="true"** in the **SPSSODescriptor,** or having **AuthnRequestsSigned="false"**.
### Handling the AssertionConsumerService Request In the **post\_assertion function**, we create the **sp** and **idp** objects to decrypt and extract the SAML response. Samlify requires that we validate the response so we call **setSchemaValidator** but we are not going deal with validation in this tutorial. For more details see [how to set up a validator](https://github.com/tngan/samlify#installation). Add the following code to http-functions.js. ```javascript import { response, ok, notFound } from 'wix-http-functions'; import { createIdp, createSp } from 'backend/create-providers.js'; import * as samlify from 'samlify'; import { authentication } from 'wix-members-backend'; const baseUrl = 'https://mysite.wixsite.com/saml-demo'; export async function post_assertion(request) { try { const [idp, sp] = await Promise.all([createIdp(), createSp()]); // we are not using the validation in our example, but if we dont call it, we'll get an error. samlify.setSchemaValidator({ validate() { return Promise.resolve('skipped'); } }); const requestBody = await request.body.text(); const samlResponse = { body: { SAMLResponse: decodeURIComponent(requestBody.split('SAMLResponse=')[1]) } } // parse and decrypt the response const parseResult = await sp.parseLoginResponse(idp, 'post', samlResponse); //extract the inResponseTo Id const inResponseTo=parseResult.extract.response.inResponseTo //extract the email address const email = parseResult.extract.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']; //create the session token to use when logging the visitor in const sessionToken = await authentication.generateSessionToken(email); return response({ status: 302, headers: { 'Location': `${baseUrl}/signed-in?session=${sessionToken}&response=${inResponseTo}` } }); } catch (error) { const body = await request.body.text(); console.error('Error in post_asc', error.message); return notFound({ status: 404 }); } } ``` #### Understanding the Code **Line 6:** Set the base URL for the site. Change this to suite your site. **Line 8:** Create the `assertion` function to handle the `post` request from the IDP. **Line10:** Create the `idp` and `sp` objects using the `createIdp` and `createSp` imported from `saml.web.js`. **Lines13-15:** Samlify requires that the response is validated, but we are not going to deal with that in this tutorial. For more details see [how to set up a validator](https://github.com/tngan/samlify#installation). Call setSchemaValidator and return a resolved promise. **Line 19:** Get the http request body in text format. **Line20:** The response body may differ between IDPs. Take the data that comes after `SAMLResponse=` using the split function and decode it. Reconstruct the response object, adding the `body` so it can be parsed. **Line 27:** Use the samlify function `parseLoginResponse` to decrypt and extract the XML that is our readable SAML response. **Line 30:** Extract the `inResponseTo` Id which will be matched to the request Id when the user is signed in. **Line 33:** Extract the email address from the SAML response XML. Note that the attribute name for the email address will differ for each IDP. **Line 36:** Use the email address to create a Wix session token using the wix-members-backend function, [`generateSessionToken()`](https://dev.wix.com/docs/velo/api-reference/wix-members-backend/authentication/generate-session-token.md). This token will allow us to log the visitor in to our Wix site. If the email address corresponds to an existing member, a session token for that member is generated. If no member exists with that email address, a new member is created along with a session token for logging that member in. **Lines 38-41:** Return a redirect URL, directing the browser to the signed-in landing page, adding the session token and `inResponseTo` Id as query fields. ### Signed-in Landing Page The visitor lands on the signed-in landing page where the session token is used to log our visitor in. Create a page called **signed-in.** Make sure that the signed-in page has the correct URL. The URL should be `https://mysite.wixsite.com/saml-demo/signed-in`. Replace `https://mysite.wixsite.com` to suite your site. Follow [this guide](https://support.wix.com/en/article/wix-editor-changing-your-page-url) to set the URL. Add a [members area](https://support.wix.com/en/article/about-members-area#adding-a-members-area) to your site, then add a **Member Profile Card** element to your signed-in page to display who the current member is. Add the following code to the signed-in page. ```javascript import wixLocationFrontend from 'wix-location-frontend'; import { session } from 'wix-storage-frontend'; import { authentication } from 'wix-members-frontend'; $w.onReady(() => { const query = wixLocationFrontend.query; const sessionToken = query.session; const responseID = query.response; const requestID = session.getItem("requestId") if (sessionToken && (responseID === requestID)) { authentication.applySessionToken(sessionToken) } else { console.error("Signin Failed", sessionToken) wixLocationFrontend.to('https://mysite.wixsite.com/saml-demo') } }); ``` #### Understanding the Code **Lines 7-9:** Retrieve the session token and `inResponseTo` id from the query parameters. **Line 10:** Retrieve the `requestId` that was stored in the session variables before signing in. **Lines 12-13:** If the `requestId` matches the `inResponseTo` Id, then we know that this browse session is the one that made the login request. If a session token exists, apply the session token to log the visitor in. **Lines 15-16:** If there is no session token, or the Ids do not match, return the visitor to the log-in page. Change the URL on line 16 to suite your site. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using OAuth SSO with Velo ## Article: Using OAuth SSO with Velo ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/using-oauth-sso-with-velo.md ## Article Content: # Using OAuth SSO with Velo Single Sign On (SSO) is a great way to make life easier for your site visitors and encourage them to register as a user on your site. Visitors can log in to your site using their Google, Facebook, or other accounts, or using their work account. This article will look at how to implement OAuth - Open Authentication protocol, which is used by many [service providers](https://en.wikipedia.org/wiki/List_of_OAuth_providers) including Google, Facebook and Twitter. To learn about implementing SSO with SAML, see [Using SAML SSO with Velo](https://support.wix.com/en/article/using-saml-sso-with-velo). ### OAuth Flow An authenticated user session is a session in which the identity of the user has been verified to the server, in the case of OAuth, by a 3rd-party. The diagram below shows the flows involved in establishing an authenticated user session on a Wix site using Google as an OAuth identity provider.  The flows are as follows: 1. The user clicks **Sign in with google**. 2. The Wix site requests an **authorization URL**. 3. The browser is forwarded to the OAuth provider using the **authorization URL,** and the user signs in. 4. The OAuth provider calls the Wix site URL, passing it an **authorization code**. 5. The Wix site backend requests an **access token** from the OAuth provider using the **authorization code**. 6. The Wix site backend requests **user information** using the **access token**. 7. The Wix site backend creates a Wix **session token** using the user's email address. 8. The Wix site backend returns a redirect URL to the OAuth provider including the **session token**. 9. The OAuth provider redirects the browser using the returned URL. 10. The browser redirects to the Wix frontend page using the URL. 11. The Wix site uses the session ID in the URL to log the user in.
### Setting up Google In the Google Cloud Platform, we're going to set up [Credentials](https://console.cloud.google.com/apis/credentials). Follow this guide to [setting up OAuth 2.0](https://support.google.com/cloud/answer/6158849?hl=en) from Google.  Copy down the **Client ID**, **Client secret** and **Authorized Redirect URL**. We're going to use them later in our code.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/09/14/378d1ac6-75c8-4670-a1a8-af1c10b98476/92e49242-ea01-4a2f-8daa-2606c8d25ca1.png)
### Using the Secrets Manager We'll use the [Wix Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) to securely store our client secret. This is more secure than pasting it to our backend code. Make sure never to expose your client secret. In the Secrets Manager, store a new secret with the following values: * **Name** : **clientSecret** * **Value**: The client secret value you retrieved from the Google Cloud Platform dashboard in the previous section. * **Description**:  Something to help you remember what this secret is used for.
If you haven't used the Secrets Manager before, here's how to use it. 1. Go to your Wix Dashboard. 2. Select **Developer Tools** and then **Secrets Manager**. 3. Click the **Store Secret** button and enter the following details: 4. **Name:** The name of the secret to be used in the code. 5. **Value:** Paste the text value of the secret. 6. Enter a brief description to remind you what this secret is used for, and click **Save**.
### Setting up Velo Before we start coding we need to install the Google APIs package so we can call its functions from our code. To install a package in the editor: 1. In the left sidebar of the editor, navigate to **Packages & Apps**. In Wix Studio, you can find this in the ![Code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/43681438c0ad6af225fb8c944cff74b4.png "Code") tab. 2. Under **npm** click **Install packages from npm.** 3. Search for "googleapis" and click **Install**. ### Adding the Code We'll need two code files: * **OAuth.web.js**. Where we will create a connection to Google and generate an authorization URL. * **http-functions.js**. To handle the callback from Google and retrieve the user's details. Note that you must use the file name **http-functions.js** to handle the web callbacks but you can call the other file any name you like. ### Getting the Authorization URL When the Visitor Clicks "Sign in to Google" **Landing page code** ```javascript import wixLocationFrontend from 'wix-location-frontend'; import {session} from 'wix-storage-frontend'; import { getAuthUrl } from 'backend/OAuth.web'; $w.onReady(function () { // handle the click event on the Sign in with Google button $w('#btnSignInToGoogle').onClick((event) => { googleSignin() }); }) export function googleSignin() { getAuthUrl() .then((result) => { const authorizationUrl=result.authUrl const state=result.state // store the state variable for later use session.setItem("requestState", state); // direct the bowser to the authorization Url wixLocationFrontend.to(authorizationUrl); }) } ``` When the site visitor clicks **Sign in to Google**, our site calls the **getAuthUrl** function in **OAuth.web.js** at line 13. We create a connection to the OAuth service provider (Google in this case) and request an authorization URL. The state variable is then stored for this session at line 18. It will be compared to the state variable that is returned with the session token to prevent anyone copying the returned URL and logging in with another browser. Our frontend then calls [**wixLocationFrontend.to**](https://www.wix.com/velo/reference/wix-location/to) at line 20 to direct the browser to the authorization URL. **OAuth.web.js** First, we retrieve our client secret at line 8, and use our API credentials to connect to Google's OAuth servers at line 17. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { google } from 'googleapis'; import { getSecret } from 'wix-secrets-backend'; import crypto from 'crypto'; export const getAuthUrl = webMethod(Permissions.Anyone, async () => { //retrieve the client secret from the Secret Manager const googleClientSecret = await getSecret('clientSecret'); const googleConfig = { clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com', clientSecret: googleClientSecret, redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth', }; // create a connection to google's authentication services const authConnection = new google.auth.OAuth2( googleConfig.clientId, googleConfig.clientSecret, googleConfig.redirect ); const scope = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ]; // generate a random state variable const state = crypto.randomBytes(16).toString('hex'); // request an authorization code URL const authUrl = authConnection.generateAuthUrl({ access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token) prompt: 'consent', scope: scope, state: state, }); return { state, authUrl }; }); ``` Note the **googleConfig** object in **OAuth.web.js**. ```js const googleConfig = { clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com', clientSecret: googleClientSecret, redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth' }; ``` These are the values from the Credentials page that we set up earlier. The **getAuth** function in the **redirect** is defined in **http-functions.js** At line 29 we generate a random value for the state variable, which will be stored by the browser, and compared to the state variable that is returned with the session token. This will prevent anyone from copying the URL with the session token and using it to log in.  At line 32, **OAuth.web.js** generates the authentication URL and returns it with the state variable, to our frontend. The frontend calls [**wixLocationFrontend.to**](https://www.wix.com/velo/reference/wix-location/to) using that URL to direct the browser to the OAuth provider - line 15 in the landing page code. At this point the visitor sees the Google sign in dialog and enters their user name and password.  Once Google authenticates the visitor, it redirects back to our site to the URL defined in **googleConfig.redirect** in our code. We also set up that URL in the Google Cloud Platform console as the authorized redirect URI. From the sample code, this will redirect to the **get\_getAuth** function which is defined in **http-functions.js**. ### Handling the Redirect from Google In **get\_getAuth,** we retrieve the client secret from the Secrets Manager at line 11. Next, we take the request URL from Google and extract the authorization code, at line 19, and the state variable at line 20. This code is used to get the access token based on our clientId and clientSecret, at line 30. Once we have the access token we use it at line 34, to get the **userInfo** object from Google. Using the email address from userInfo, at line 45 we generate a session token using the wix-members-backend function [**generateSessionToken**](https://www.wix.com/velo/reference/wix-members-backend/authentication/generatesessiontoken). This token will allow us to log the user in to our Wix site. If the email address corresponds to an existing member, a session token for logging in that member is generated. If there is no existing member with the specified email address, a new member is created and a session token for logging in that member is generated. **http-functions.js**. ```javascript import { response } from 'wix-http-functions'; import { google } from 'googleapis'; import { authentication } from 'wix-members-backend'; import { getSecret } from 'wix-secrets-backend'; import { fetch } from 'wix-fetch'; //google calls this function with a get, after the user signs in export async function get_getAuth(request) { // retrieve the client secret from the Secrets Manager const googleClientSecret = await getSecret('clientSecret'); const googleConfig = { clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com', clientSecret: googleClientSecret, redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth' }; //get the authorization code and state variable form the request URL const code = await request.query.code const state= await request.query.state // create a connection to google's authentication services const auth2 = new google.auth.OAuth2( googleConfig.clientId, googleConfig.clientSecret, googleConfig.redirect ); //get the access token from the request with the authorization code we got from google const data = await auth2.getToken(code); const tokens = data.tokens; //get the user info using the access token const userInfoRes = await fetch(`https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=${tokens.access_token}`, { "method": "get" }) if (!userInfoRes.ok) { console.log("could not get user info using access token") } //extract the user's email and profile picture URL const userInfo = (await userInfoRes.json()) const userEmail = userInfo.email const profilePicture = userInfo.picture //now that we have the email we can use it to generate a Wix session token to use in the frontend const sessionToken = await authentication.generateSessionToken(userEmail); //return the url, session token, state variable, and profile picture to google to redirect the browser to our logged in page. return response({ status: 302, headers: { 'Location': `https://mywix987.wixsite.com/sso-example/loggedin?sessiontoken=${sessionToken}&responseState=${state}&profilepic=${profilePicture}` } }); } ``` ### Back to Our Site Page The last step in the **http-functions.js** process at line 49, is to return a 302 status to Google, with a redirect URL to our site's signed-in page. The URL includes the session token so that we can automatically log the user in and the state variable to protect against copying the URL. Google passes this back to the browser where our site page code uses the token to log the user in. On our signed-in page, we take the URL and extract the query parameters, including the session token and the response state variable at lines 8-11. We then remove the query parameters from the URL to make it harder to copy.  At line 17 we retrieve the state variable that we saved before signing in. On line 20, we compare the saved state variable to the state variable returned in the URL. If someone managed to intercept the returned URL, and paste it into a browser, they would not have the value of the saved state variable. The comparison would fail and they would not be signed in. If the saved state is the same as the returned state, then this is the same browser that made the sign in request, so we can sign the user in, using the wix-members-frontend [**applySessionToken**](https://www.wix.com/velo/reference/wix-members/authentication/applysessiontoken) function, at line 21 below. Create a new page called **signed-in.** Make sure that your signed-in page has the correct URL. The URL should be `https://mywix987.wixsite.com/sso-example/signed-in`, replacing **mywix987.wixsite.com** to suit your site. Follow [this guide](https://support.wix.com/en/article/wix-editor-changing-your-page-url) to set the URL. ```javascript import wixLocationFrontend from 'wix-location-frontend'; import { authentication } from 'wix-members-frontend'; import {session} from 'wix-storage-frontend'; $w.onReady(function () { // get the session token and the profile pic URL const query = wixLocationFrontend.query; const sessionToken = query.sessionToken; const userPic = query.profilePic; const responseState = query.responseState; // remove the query parameters from the URL so they can't be copied. wixLocationFrontend.queryParams.remove(['sessionToken', 'profilePic','responseState']); // retrieve the state variable data we saved before signing in const requestState=session.getItem("requestState") // if the requestState matches the responseState, log the user in using the session token if (sessionToken && (requestState === responseState)) { authentication.applySessionToken(sessionToken).then(() => { //set the profile picture on our landing page $w('#image1').src=userPic; }) } }) ``` ### Adapting the Code for Your Site Make the following changes to customize the code to work on your site: * **OAuth.web.js**  Line 10 - googleConfig - **client id** and **redirect** must be changed for your site URL and Google credentials. * **http-functions.js**.  Line 12 - googleConfig - **client id** and **redirect** must be changed for your site URL and Google credentials.     Line 51 - The **Location** URL must be changed to suit your site. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding a Wishlist to a Wix Stores Site ## Article: Adding a Wishlist to a Wix Stores Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-wishlist-to-a-wix-stores-site.md ## Article Content: # Velo Tutorial: Adding a Wishlist to a Wix Stores Site >**Note:** > This tutorial and its steps are based on a Wix Editor site. If you'd prefer to use Wix Studio, learn more about [working with the code panel in Wix Studio](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md). This article describes how you can use Velo to add wishlist functionality to a Wix Stores site. Throughout this article we're going to use [this site](https://www.wix.com/code-examples/wishlist-vw) to illustrate the process. You can open the site in the Wix Editor to work with the [template](https://editor.wix.com/html/editor/web/renderer/new?siteId=62ddd72a-7d89-44de-8ce4-ddf15e414b56&metaSiteId=5f8ae7eb-e6fc-4f07-bd91-384921eed30e). We're going to explain how we set up the sample site and the code we added to make it work. You can also use the built-in wishlist feature in any editor. Learn more [here](https://support.wix.com/en/article/wix-stores-adding-and-setting-up-a-wishlist).
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/07/25/35e1d9a8-3951-495e-9ede-eddf05e4617f.png)
### Overview **In our site we added the following:** * A My Wishlist page to the Member Pages group with a repeater that displays the member's wishlist. It also has a group of elements that are displayed if the member's wishlist is empty. * A products-wishlist collection with 2 fields:  * User Id: This stores the ID of the currently signed-in member. * Product: This is a reference field that points to the product the member added to their wishlist. * Icons on the Shop Product Page that show if a product is already in the member's wishlist. We also use these icons as a toggle to let the member add or remove the item from their wishlist.  **Then we added code to do the following:** * When the Product Page loads, check to see if the current product is associated with the member in the products-wishlist collection and display the corresponding icon. * Set up the icons so that when they are clicked they either add or remove the product from the products-wishlist collection. * When the My Wishlist page loads, query the products-wishlist collection for all the products associated with the current member and display them in the repeater. Hide the repeater if there are no associated products. * Prompt the visitor to log in if they aren't and they try to add or remove an item from their wishlist. ### Step 1: Set up the Site For this example, you'll need some products stored in a collection. We added Wix Stores to our site, which automatically adds a Stores/Products collection to our site, but you can also create your own collection of products without Wix Stores. >**Note** > You may need to save or publish the site and refresh your browser to view Stores collections in the Database. This is what some of the data in our Products collection looks like:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/27/ab85421f-1c74-4588-8119-69b341e504e3/7fd6f72c-409f-47d2-b4e8-faa0ceff0aec.png)
We also added a products-wishlist collection. Click the **Databases** button in the **Velo Sidebar**, then hover over **Content Collections** and click the plus icon to create the collection.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/29/1116c9b2-a2c4-4157-8b9f-47f4b68d920c/5e7effee-a13c-4725-a6e7-c13c679cd3ef.png)
The products-wishlist collection has two fields: - **Product**: A reference to the product the member added. - **User Id**: The ID of the member who adds a product to their wishlist.
The products-wishlist collection maintains all the wishlist information for all the site members. Each item in the collection associates a member's ID with a product they selected to add to their wishlist. With that information in the collection, we can then query the collection for a member's ID and the results represent the member's wishlist products. In our example we made User Id the Primary field and deleted the Title field from the collection. You can see a copy of the data from the Collections, Products, and products-wishlist collection [here](https://github.com/jeffreya/WixCode-RelatedProducts-data). The Products and Collections collections are created automatically when you have a site with Wix Stores. Because these collections are read-only, you must use the Store Manager to create your product list.  ### Step 2: Set up the Shop Product Page On the Shop Product Page we added: * A group of elements that prompt the visitor to log in if they need to. * Heart icons that tell the member if the product is in their wishlist. The icons also work as a toggle to add or remove the item from their wishlist. > **Note**: > In the image below the two icons are displayed side-by-side. > On the actual site, they're overlapping and the code controls which is displayed.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/06/20/65fbdc3a-136e-4880-a07d-07071bf5967a.png)
### Step 3: Create and Set Up the My Wishlist Page To set up the My Wishlist page, complete the following steps: 1. Click the **Pages** button, then **Member Pages**, then **\+ Add a Member Page**. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/29/7a2c194f-a282-47db-aa4a-5a8b08158823/a8930133-07c7-4efc-8aec-4d36cecbdf76.png) 2. Make the My Wishlist page a **Private Page**. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/29/1a38bede-0c34-4b4b-94d9-a7009aeadf03/52882f34-9348-4d7c-bca1-1ee9cf48d5ab.png) 3. Then rename the new member page by accessing the page's settings. Change the name to My Wishlist. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/29/655d684b-5515-496e-86e2-b6abacd01a10/579014a3-878d-45f9-9473-cea418f8115c.png) 4. Access and edit the My Wishlist page by clicking the **Page Code** button in the **Velo Sidebar**, then **My Wishlist** in the **Member's Area (members)** dropdown. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/29/4b96f6fe-830f-474b-b494-8c6e5ff90e35/855fe0d8-450e-4d69-a278-f8a35e86e7c9.png) On the My Wishlist page we added: * A container box that contains a text box and a button labelled Go Shopping. The container box is set to "Collapsed on load" in the Properties & Events panel and is only displayed if the member's wishlist is empty or if they aren't signed in. * A repeater that will display the wishlist. Because the container box is set to be collapsed if there's a wishlist, the repeater moves up to take its space when there is a wishlist.
### Step 4: Create the Add to Wishlist Function on the Product Page On the Product Page we start by importing the modules we need to prompt visitors to log in and to work with our collections in code. Then we create a function that builds and inserts the item into the products-wishlist collection. This function is called by the click event handler for when the member clicks the empty heart icon. We'll define that function in the next step. >**Note:** > Throughout this example we use the [async/await](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) JavaScript functionality. ```javascript import wixData from 'wix-data'; import wixMembersFrontend from 'wix-members-frontend'; let product; let member = await wixMembersFrontend.currentMember.getMember(); $w.onReady(async function () { product = await $w('#productPage1').getProduct(); }); async function addToWishlist() { let wishListItem = { product: product._id, userId: member._id }; let result = await wixData.insert("Wishlist", wishListItem); } ``` #### Understanding the Code **Lines 1-2**: Import the modules we need to work with [Wix Data](https://www.wix.com/code/reference/wix-data.html) and [Wix Members Frontend](https://www.wix.com/velo/reference/wix-members-frontend) libraries. **Line 4**: Define the `product` variable. **Line 5**: Set the `member` variable to the current member. **Lines 7-9**: Get the current product from the Product page. **Lines 11-15**: Create the `addToWishList` function. Build the `wishListItem` object. This object contains the product ID for the current product and the ID of the member currently logged in. **Line 17**: Insert the `wishListItem` object to the `products-wishlist` collection. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#productPage1` * `wishlist` ### Step 5: Create the notInWishList\_click Function on the Product Page `notInWishList_click` is the function that runs when the empty heart icon is clicked. It hides the empty heart icon, displays the full icon, and then calls the `addToWishList` function we defined in step 4 that handles adding the product to the member's wishlist. To create the function, on the Product Page, select the empty heart icon in the editor and then use the Properties & Events panel to add a click event to it. Where it says "//Add your code for this event here:" add the following code: ```javascript export function notInWishList_click(event, $w) { if (wixMembersFrontend.authentication.loggedIn()) { addToWishlist(); $w('#notInWishList').hide('fade', {duration: 100}); $w('#inWishList').show('fade', {duration: 100}); } else $w('#loginMessage').show(); } ``` #### Understanding the Code **Line 2**: Check to see if the current visitor is logged in. **Line 3**: If a member is logged in, call `addToWishList`, which adds the current product to the member's wishlist. **Lines 4-5**: Hide the empty heart and show the full heart, indicating to the member that the product is now in their wishlist. **Lines 7-8**: Prompt the visitor to log in if they aren't. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#notInWishList` * `#inWishList` * `#loginMessage` ### Step 6: Create the removeFromWishList Function on the Product Page On the Product page we create a function that removes the item from the products-wishlist collection. This function is called by the click event handler for when the member clicks the full heart icon. We'll define that function in the next step. We'll also show and hide the appropriate icons, similar to what we did in step 5. ```javascript async function removeFromWishlist() { let wishListResult = await wixData.query("products-wishlist") .eq("product", product._id) .eq("userId", member._id) .find(); if (wishListResult.length > 0) { $w('#notInWishList').show('fade', {duration: 100}); $w('#inWishList').hide('fade', {duration: 100}); await wixData.remove("Wishlist", wishListResult.items[0]._id) } } ``` #### Understanding the Code **Lines 2-5**: Run a query on the `products-wishlist` collection for the current product for the signed in member. **Line 7**: Check that the item exists in `products-wishlist` for the current member (results of the query are greater than 0). **Lines 8-9**: Display the empty heart icon and hide the full heart icon. **Line 10**: Call `wixData.remove` to remove the current product from the `products-wishlist` collection. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `wishlist` * `product` * `userID` * `#notInWishlist` * `#inwishlist` ### Step 7: Create the inWishList\_click Function on the Product Page `inWishList_click` is the function that runs when the member clicks the full heart icon and wants to remove a product from their wishlist. It checks to see that a member is logged in and then calls the `removeFromWishlist` function we defined in step 6. To create the function, select the full heart icon in the editor and then use the Properties & Events panel to add a click event to it. Where it says "//Add your code for this event here:" add the following code: ```javascript export function inWishList_click(event, $w) { if (wixMembersFrontend.authentication.loggedIn()) removeFromWishlist(); } ``` #### Understanding the Code **Line 2**: Check if the current visitor is logged in. **Line 3**: Call `removeFromWishlist` to remove the current product from the member's wishlist and change the displayed icons. There are no identifiers you would need to change here to make this code work on your site. ### Step 8: Create the checkWishlist Function on the Product Page `checkWishlist` is the function that runs when the Product Page loads. It checks to see if the current product is in the member's wishlist. Here also the appropriate icon is displayed depending on the product's wishlist status. If the current visitor isn't logged in, the empty heart icon is also displayed. First, add the code below to the Product Page. ```javascript async function checkWishlist() { if (wixMembersFrontend.authentication.loggedIn()) { let wishListResult = await wixData.query("products-wishlist") .eq("product", product._id) .eq("userId", member._id) .find(); if(wishListResult.items.length > 0) { $w('#inWishList').show('fade', {duration: 100}); } else { $w('#notInWishList').show('fade', {duration: 100}); } } else { $w('#notInWishList').show('fade', {duration: 100}); } } ``` #### Understanding the Code **Line 2**: Check that the current visitor is logged in. **Lines 3-6**: Query the `products-wishlist` collection for the current product and the signed in member. **Line 8**: Check that the product exists in the wishlist for the current member (results of the query are greater than 0). **Lines 9-12**: If the product is in the member's wishlist, display the full heart icon. If not, display the empty heart icon. **Lines 15-16**: If the current visitor isn't logged in, display the empty heart icon. Then, add this code to the onReady function for the Product Page where it says //TODO: write your page related code here... ```javascript checkWishlist(); ``` #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `wishlist` * `product` * `userId` * `#inWishList` * `#notInWishList` ### Step 9: Create the loginMessageClick Function on the Product Page When we set up our site we added a group of elements to the Product Page that prompt the visitor to log in if they aren't already. Now we need to create the `loginMessageClick` function that displays the login prompt if the visitor clicks an element in that group. The `loginMessageClick` function displays the login prompt to the visitor, and we want it to run when a visitor clicks anywhere in the group. We can't use the Properties & Events panel to add a click event handler to a group, so instead we'll create that event handler directly in the code. We'll create this event handler in the `onReady` function for the Product Page. First, add the following to the Product Page's code: ```javascript async function loginMessageClick() { let options = {"mode": "login"}; $w('#loginMessage').hide(); await wixMembersFrontend.authentication.promptLogin(options); } ``` #### Understanding the Code **Line 2**: Create an `options` object that will be sent to the `promptLogin` function. This sets the login to prompt the visitor to log in as opposed to sign up. **Line 3**: Hide the `loginMessage` group of elements. **Line 4**: Call `wixMembersFrontend.authentication.promptLogin()` with the `options` object. Then, add this code to the onReady function for the Product Page where it says //TODO: write your page related code here... ```javascript $w('#loginMessage').onClick(loginMessageClick); ``` #### Understanding the Code **Line 1**: Create an `onClick` event for the `loginMessage` group of elements that calls the `loginMessageClick` function. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#loginMessage` ### Step 10: Create the loadWishlist Function on the My Wishlist Page When we set up our site we added a My Wishlist page to the Member Pages group of pages. This page has a repeater to display their wishlist when it has products, and a group of elements that displays a message to the member if their wishlist is empty. First let's import the modules we need to work with the Wix Data, Wix Members Frontend, and Wix Location libraries. This code needs to be at the top of the page. ```javascript import wixData from 'wix-data'; import wixMembersFrontend from 'wix-members-frontend'; import wixLocationFrontend from 'wix-location-frontend'; ``` Now we need to add the code to that page that will either display the wishlist in the repeater or the empty wishlist message. ```javascript async function loadWishlist(){ let member = await wixMembersFrontend.currentMember.getMember(); let wishlistResult = await wixData.query("products-wishlist") .eq("userId", member._id) .include('product') .find() if (wishlistResult.length > 0) { $w("#wishlist").expand(); $w("#emptyWishlist").collapse(); $w("#wishlist").data = wishlistResult.items; $w('#wishlist').onItemReady(myItemReady); } else { $w("#wishlist").collapse(); $w("#emptyWishlist").expand(); } } ``` #### Understanding the Code **Lines 3-6**: Query the `products-wishlist` collection for all the products in the member's wishlist. Use `.include` to include the referenced product. **Line 8**: Check if there are products in the member's wishlist (query results are greater than 0). **Lines 9-10**: Display the repeater and hide the empty wishlist message. **Line 11**: Set the data for the repeater to be the results of the query. **Line 12**: Set the `onItemReady` function for the repeater to `myItemReady`. We'll define that function in step 11. **Line 14-16**: If the query on the `products-wishlist` collection returns no results, hide the repeater and display the empty wishlist message. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `wishlist` * `userId` * `product` * `#wishlist` * `#emptyWishlist` * `#loginMessage` ### Step 11: Create the myItemReady Function on the My Wishlist Page `myItemReady` is the [onItemReady](https://www.wix.com/code/reference/$w.Repeater.html#onItemReady) function for the repeater. It's the function that runs when a new repeater item is created, which we call in line 11 of the code in step 10. Add the following code to the My Wishlist page: ```javascript function myItemReady($w, wishlistItem){ let product = wishlistItem.product; $w('#productImage').src = product.mainMedia; $w('#name').text = product.name; $w('#price').text = product.formattedPrice; $w('#productImage').onClick(()=>{ wixLocationFrontend.to(product.productPageUrl); }); $w('#removeItem').onClick(removeItem(wishlistItem._id)); } ``` #### Understanding the Code **Line 2**: Get the current product from the wishlist for the repeater item. **Lines 3-5**: Bind each element in the repeater to the corresponding data for the product. **Lines 6-7**: Set the `onClick` property of the `productImage` element to open the product page for the specific product. **Line 9**: Add a click event handler for the `removeItem` icon for the product displayed in the repeater item, to call the `removeItem` function. This will remove the item from the member's wishlist if they click the `removeItem` icon. We'll define this function in the next step. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#productImage` * `#name` * `#price` * `#removeItem` ### Step 12: Create the removeItem Function on the My Wishlist Page `removeItem` removes an item from a member's wishlist. It's the function that runs when a member clicks the 'X' for a product in their wishlist, which we call in line 9 of the code in step 11. ```javascript function removeItem(id) { return async function() { await wixData.remove('products-wishlist', id); loadWishlist(); } } ``` #### Understanding the Code **Line 3**: Remove the current product from the member's wishlist. **Line 4**: Call `loadWishlist` to reload the wishlist after the product was removed. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `wishlist` ### Step 13: Display the Member's Wishlist or Hide It for Non-Members If the site visitor is signed in we need to call `loadwishList` to display their wishlist, if they have one. If they aren't signed in, we hide the wishlist and display the empty wishlist message. We'll add this code to the `onReady` function of the My Wishlist page. ```javascript $w.onReady(async function () { if(wixMembersFrontend.authentication.loggedIn()) { loadWishlist(); } else { $w('#wishlist').collapse(); $w('#emptyWishlist').expand(); } }); ``` #### Understanding the Code **Lines 2-3**: Check to see that a member is logged in and, if they are, call `loadWishlist` to load their wishlist. Because `loadwishlist` is returned by the `onReady` function, the function stops running here. **Lines 5-7**: Only if the visitor isn't logged in, hide the wishlist and display the empty wishlist message. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#wishList` * `#emptyWishlist` ### Next Steps * Open [this example](https://editor.wix.com/html/editor/web/renderer/new?siteId=62ddd72a-7d89-44de-8ce4-ddf15e414b56&metaSiteId=5f8ae7eb-e6fc-4f07-bd91-384921eed30e) in the Wix Editor to work with the template. * Publish the site and refresh your browser so the Stores collections appear in the Database. * Try other Velo Store examples: * [Adding a Related Products Area to a Wix Store Product Page](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-related-products-area-to-a-wix-store-product-page.md) * [Adding Multiple Items to the Cart in a Wix Stores Site](https://support.wix.com/en/article/adding-multiple-items-to-the-cart-in-a-wix-stores-site) * [Adding a Gift Quiz to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) * [Adding a Product Configurator to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md) * [Adding Ratings and Reviews to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding Ratings and Reviews to a Wix Stores Site ## Article: Adding Ratings and Reviews to a Wix Stores Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md ## Article Content: # Velo Tutorial: Adding Ratings and Reviews to a Wix Stores Site > **Note:** > This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#finding-your-way). This article describes how you can use Velo to add ratings and reviews to a Wix Stores site. Throughout this article we're going to use [this site](https://www.wix.com/code-examples/shop-reviews-vw/product-page/pom-pom-body-mist) to illustrate the process. We're going to explain how we set up the sample site and the code we added to make it work.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/10/02/288a6751-a709-4af5-8b23-38479a226200.png)
### Overview **In our site we added the following:** * A Review Box popup with user input elements for review submissions. * A repeater on the Product page to display the individual reviews. * A group of elements on the Product page that display the average rating results and the percentage of members who recommend the product. * A reviews collection to store review information. * A review-stats collection to store rating statistics for each product.  **Then we added code to do the following:** * When the Product page loads, display the current statistics under the product description and display the reviews in the repeater. * When the reviewer clicks the "Write a Review" button, open the Review Box popup. * When a reviewer posts a review in the Review Box popup, save the new review data to the reviews collection, update the statistics in the review-stats collection, close the popup, and refresh the reviews and statistics in the Product page. > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. ### Step 1: Set up the Site To recreate this functionality, you'll need to have Wix Stores added to your site with some products. Once you add Stores to your site, you'll see pages and collections automatically added to your site.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/8741b360-a9b7-48e1-aacb-f5ec209c9d5c/219ec892-04c3-40b6-9393-101d97300b47.png)
>**Note** > You may need to save or publish the site and refresh your browser to view the Stores collections in the Database. This is what some of the data in our site looks like:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/31/3e666892-ef06-4012-b36a-654e3a99d4cc.png)
The Products and Collections collections are created automatically when you have a site with Wix Stores. Because these collections are read-only, you must use the Store Manager to create your product list. In this example we don't work with the Collections collection, although it is in our sample site's store. We also added a Review Box popup to the site, and review-stats and reviews collections to our database. We'll describe the Review Box popup's setup later in this article.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/b3d2c337-d237-40fa-9fb5-b66aac156405/f060268e-e7a8-4490-8621-8a1a061f081e.png)
The review-stats collection has the following fields: * rating: The sum of all ratings the product received * count: The number of times the product was rated * recommended: The number of times the product was recommended * ID: The ID of the product (hidden by default)
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/fb3ce682-f32d-4ece-bb15-9ca0679d614b/77f27182-dfd2-4e08-a572-8d2cf19766ae.png)
We use the statistics stored in the review-stats collection to calculate the number of reviews, the average rating, and the recommended percentage for each product. The reviews collection has fields to store the review information entered via the Review Box popup, plus a field that references the item in the Products collection.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/1be2cbee-c727-4e0e-b254-8f07dbd32896/36e0629c-6113-4226-87b7-18234b3590ce.png)
We use the data stored in the reviews collection to display each review on the Product page, and to calculate the statistics for each product stored in the review-stats collection. ### Step 2: Set up the Shop Product Page On the Shop Product Page we added: * A group of elements that display the average rating results: * A Ratings Display showing the number of ratings and the average rating for this product * Text displaying the percentage of reviewers who recommended the product * A thank you message, which is displayed after a review is added * A Write a Review button, which opens the review form in the Review Box popup.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/09/20/9d9b9c36-86db-4df2-b826-80ada8437ac7.png)
We also added: * A Reviews dataset for displaying review details stored in the reviews collection. * A repeater to display the reviews. Each review contains: * Text elements for the reviewer's name, location, review title, and review text * An optional review image * A ratings display to show the individual reviewer's rating * Text stating whether the reviewer recommends the product * Text containing the review submission date and time * "load more" text at the bottom of the page for loading additional reviews.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/09/20/9392034d-9d05-441c-a096-7421c8fbb8ef.png)
Note that there are two types of ratings displayed on the Product page: * An average rating of the product and overall percentage of reviewers who recommended the product. This is displayed beneath the product description. * Each individual reviewer's rating and recommendation (or not) of the product, displayed in each review. ### Step 3: Set Up the Review Box Popup
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/aec8294e-c9f8-40e7-9d6e-9d49820e9cf2/c8aa46f5-d177-41fa-b3e8-cb4083e3650b.png)
In the Review Box popup we added: * A SubmitReviews dataset to connect the data entered by the reviewer to the reviews collection. * A container box to hold all the input elements. * Two sets of radio buttons for rating and recommending the product. * Input elements to collect the reviewer's identification information and their review of the product. * A "Choose File" upload button for uploading an optional review image. * A "Post Review" button for submitting the review. Data is sent to the reviews and review-stats collections.
### Step 4: Prepare the Product Page On the Product Page we start by importing the modules we need to work with data collections and popups in our code. Then we define a global variable to represent the current product. Finally, we run the `initReviews` function to load the current product's ratings and reviews on the page. >**Note:** > Throughout this example we use the [async/await](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) JavaScript functionality. ```javascript import wixData from 'wix-data'; import wixWindowFrontend from 'wix-window-frontend'; import wixLocationFrontend from 'wix-location-frontend'; let product; $w.onReady(async function () { product = await $w('#productPage1').getProduct(); initReviews(); wixLocationFrontend.onChange(async (location) => { product = await $w('#productPage1').getProduct(); initReviews(); }) }) ``` #### Understanding the Code **Lines 1-3**: Import the modules we need to work with [Wix Data](https://www.wix.com/velo/reference/wix-data.html), [Wix Window Frontend](https://www.wix.com/velo/reference/wix-window.html), and [Wix Location Frontend](https://www.wix.com/velo/reference/wix-location) libraries. **Line 5**: Define the global `product` variable. **Line 7**: When the page loads, do the following: **Line 8**: Using the `getProduct` function on the current product page, set the `product` variable to the currently displayed product. **Line 9**: Run the `initReviews` function to load the current product's ratings and reviews. **Line 11**: Some functionalities change the product without refreshing the page (for example, [next/previous buttons](https://support.wix.com/en/article/velo-tutorial-creating-previous-and-next-buttons-for-a-dynamic-item-page-with-code) and [related products gallery](https://support.wix.com/en/article/wix-stores-adding-a-related-products-gallery)). This change triggers Wix Location Frontend's [onChange()](https://www.wix.com/velo/reference/wix-location/onchange) API, then: **Line 12**: Update the `product` variable for the newly displayed product. **Line 13**: Run the `initReviews` function to load the current product's ratings and reviews. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#productPage1` ### Step 5: Create the initReviews Function on the Product Page The `initReviews` function filters the `Reviews` dataset to contain only reviews of the currently displayed product, and then displays the product's reviews and statistics. >**Note** > There are two ways to filter a dataset: using code (as in this example) or via the [Dataset Settings](https://support.wix.com/en/article/filtering-and-sorting-datasets) panel of the dataset. ```javascript async function initReviews() { await $w('#Reviews').setFilter(wixData.filter().eq('productId', product._id)); showReviews(); loadStatistics(); } ``` #### Understanding the Code **Line 2**: Chain an `eq` function to `setFilter` to filter out all items in the `Reviews` dataset where the product ID doesn't match the ID of the currently displayed product. **Line 3**: Run the `showReviews` function to display all filtered reviews. **Line 4**: Run the `loadStatistics` function to load and display statistics for the current product. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#Reviews` * `productId` ### Step 6: Create the loadStatistics Function on the Product Page The `loadStatistics` function gets and displays statistics for the current product. ```javascript async function loadStatistics() { const stats = await wixData.get('review-stats', product._id); if (stats) { let avgRating = (Math.round(stats.rating * 10 / stats.count) / 10); let percentRecommended = Math.round(stats.recommended / stats.count * 100); let ratings = $w('#generalRatings'); ratings.rating = avgRating; ratings.numRatings = stats.count; $w('#recoPercent').text = `${percentRecommended} % would recommend`; $w('#generalRatings').show(); } else { $w('#recoPercent').text = 'There are no reviews yet'; } $w('#recoPercent').show(); } ``` #### Understanding the Code **Line 2**: Use the current product's ID to get the product's statistics from the `review-stats` collection and assign them to the `stats` variable. **Line 3**: Check whether there are any statistics related to the current product, indicating that one or more users have rated the product. If there are stats, do the following: **Line 4**: Calculate the average rating of the product by dividing the sum of all ratings the product received from all reviewers by the number of times the product was rated, multiplying by ten, and rounding the number. **Line 5**: Calculate the percentage of people who recommended the product by dividing the total number of recommendations the product received by the number of times the product was rated, multiplying by 100, and rounding the number. **Line 6**: Get the `generalRatings` ratings element and assign it to the `ratings` variable. **Line 7**: Set the ratings element's rating value to the average rating calculated above. **Line 8**: Set the ratings element's total number of ratings from the `count` field in the `review-stats` collection for the current product. **Line 9**: Set the text that displays the recommended percent. **Line 10**: Show the ratings element. **Lines 11-14**: If there are no stats (indicating that no reviewers have rated the product yet), display text stating that there are no reviews yet. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `review-stats` * `#generalRatings` * `#recoPercent` ### Step 7: Create the reviewsRepeater\_itemReady Function on the Product Page We selected the `reviewsRepeater` element and, in the Properties & Events panel, added an [`onItemReady`](https://www.wix.com/velo/reference/$w.Repeater.html#onItemReady) [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). In [Step 5](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) the `initReviews` function filters the `Reviews` dataset to contain only reviews of the currently displayed product. Each filtered item (row) in the collection is passed to the `reviewsRepeater_itemReady` event handler as the `itemData` parameter. After the page loads and the filtered dataset connects to the repeater, `reviewsRepeater_itemReady` loads data from the collection into each element in the repeater. ```javascript export function reviewsRepeater_itemReady($w, itemData, index) { if (itemData.recommends) { $w('#recommendation').text = 'I recommend this product.'; } else { $w('#recommendation').text = "I don't recommend this product."; } if (itemData.photo) { $w('#reviewImage').src = itemData.photo; $w('#reviewImage').expand(); } $w('#oneRating').rating = itemData.rating; let date = itemData._createdDate; $w('#submissionTime').text = date.toLocaleString(); } ``` #### Understanding the Code **Line 2**: Check if the reviewer recommended the product using the boolean `recommends` field in the `reviews` collection of the current item. **Lines 3-4**: Display text that states whether the reviewer recommended or didn't recommend the product. **Lines 7-9**: If the reviewer uploaded a photo, set the image URL from the item photo and expand the image. **Line 11**: Set the rating value for the review in the `oneRating` ratings display as the rating of the current item. **Line 12**: Get the date the review was submitted and assign it to the `date` variable. **Line 13**: Use the `toLocalString` function to format the date text according to date format settings on the visitor's computer. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#recommendation` * `#reviewImage` * `#oneRating` * `#submissionTime` ### Step 8: Create the showReviews Function on the Product Page `showReviews` expands or collapses the review strip that displays the reviews depending on whether any reviews were submitted. ```javascript export function showReviews() { if ($w('#Reviews').getTotalCount() > 0) { $w('#reviewsStrip').expand(); } else { $w('#reviewsStrip').collapse(); } } ``` #### Understanding the Code **Line 2**: Use the `getTotalCount` function to check whether there are any reviews in the `Reviews` dataset for the current product. **Lines 3-5**: If there are reviews, expand the review strip. If there are no reviews, collapse the review strip. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#Reviews` * `#reviewsStrip` ### Step 9: Create the addReview\_click Function on the Product Page `addReview_click` runs when the reviewer clicks the "Write a Review" button. We selected the "Write a Review" button and, in the Properties & Events panel, added an [`onClick`](https://www.wix.com/velo/reference/$w.Text.html#onClick) [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). The function opens the Review Box popup, sends it the current product's ID, and waits for the popup to close. After the popup has closed, the function refreshes the `Reviews` dataset so the new reviews appear on the page, reloads the product statistics to reflect the new rating, and displays a thank you message. ```javascript export async function addReview_click(event, $w) { const dataForLightbox = { productId: product._id }; let result = await wixWindowFrontend.openLightbox('Review Box', dataForLightbox); $w('#Reviews').refresh(); setTimeout(() => { loadStatistics(); }, 2000); $w('#thankYouMessage').show(); } ``` #### Understanding the Code **Lines 2-3**: Create a `dataForLightbox` object containing the current product's ID to be sent to the Review Box popup. **Line 5**: Open the Review Box popup, send it the product ID object created above, and wait for it to close. **Line 6**: After the review popup is closed, refresh the `Reviews` dataset so the new review appears on the page. **Line 7-9**: Reload the current products statistics to reflect the new rating. **Line 10**: Show a thank you message. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#Reviews` * `#thankYouMessage` ### Step 10: Create the resultsPages\_click Function on the Product Page `resultsPages_click` runs when the reviewer clicks the "load more" text at the bottom of the Product page. We selected the "load more" text and, in the Properties & Events panel, added an [`onClick`](https://www.wix.com/velo/reference/$w.Text.html#onClick) [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). When the visitor clicks the text, the `resultsPages_click` event handler loads another page (chunk) of reviews into the `reviewsRepeater`. >**Note** > The number of reviews displayed in each page (chunk) of the repeater is defined in the Reviews dataset. Click the dataset, click **Manage Dataset**, and enter the **Number of items to display**. ```javascript export function resultsPages_click(event, $w) { $w('#Reviews').loadMore(); } ``` #### Understanding the Code **Lines 1-2**: When the event handler is triggered, the `Reviews` dataset loads the next page (chunk) of reviews using the `loadMore` function. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#Reviews` ### Step 11: Prepare the Review Box Popup In the Review Box popup, we started by importing the modules we need to work with data collections and popups in our code. Then we declare a variable to store the current product ID.  When the popup finishes loading, we get the current product ID passed to the popup in [Step 9](#step-9-create-the-addreview_click-function-on-the-product-page) via the `openLightbox` function. After reviewers complete their review and click the "Post Review" button, the new review data is sent to the `SubmitReview` dataset. Before the new review is saved to the `reviews` collection, we use the `onBeforeSave` event handler to check whether the reviewer rated the product. If the user did not rate the product, an error message is displayed and the review is not saved. After the new review is saved to the `reviews` collection, we use the `onAfterSave` event handler to update the statistics for the current product, and to close the popup. First, add the following to the popup page's code: ```javascript import wixWindowFrontend from 'wix-window-frontend'; import wixData from 'wix-data'; let productId; ``` #### Understanding the Code **Lines 1-2**: Import the modules we need to work with [Wix Data](https://www.wix.com/velo/reference/wix-data.html) and [Wix Window Frontend](https://www.wix.com/velo/reference/wix-window.html) libraries. **Line 4**: Declare a global variable to store the product ID. Then, add the following code to the onReady function: ```javascript $w.onReady(function () { productId = wixWindowFrontend.lightbox.getContext().productId; $w('#SubmitReviews').onBeforeSave(() => { if ($w('#radioRating').value === '') { $w('#rateError').show(); return Promise.reject(); } $w('#SubmitReviews').setFieldValues({ productId, rating: $w('#radioRating').value, recommends: $w('#radioGroup1').value }); }); }); ``` #### Understanding the Code **Line 3**: When the page loads, use the `getContext` function to get the object passed to the popup when it was opened. In this case, the object contains the current product ID. **Line 5**: Set the action that occurs before the new review is saved to the `reviews` collection via the `SubmitReviews` dataset. **Line 6**: Check if the reviewer rated the product. **Lines 7-8**: If the reviewer did not rate the product, show the `rateError` error message and do not save the new review. **Line 11**: If the reviewer did rate the product, use the `setFieldValues` function to update the `SubmitReviews` dataset. **Lines 12-14**: Update the dataset item with the input element values. The item will then be saved to the `reviews` collection. And finally, add the following code to the onReady function: ```javascript $w('#SubmitReviews').onAfterSave(async () => { await updateStatistics($w('#radioGroup1').value); wixWindowFrontend.lightbox.close(); }); ``` #### Understanding the Code **Line 1**: Set the action that occurs after the new review is saved to the `reviews` collection via the `SubmitReviews` dataset. **Line 2**: Update the product's statistics using the `updateStatistics` function. The reviewer's recommendation (or not), as stored in the `radioGroup1` radio buttons, is sent as a parameter to the function. **Line 3**: After the statistics are updated, close the popup to return the reviewer to the Product page. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `productID` * `#SubmitReviews` * `#radioRating` * `#rateError` * `#radioGroup1` ### Step 12: Create the updateStatistics Function in the Review Box Popup This function updates the statistics stored in the `review-stats` collection to include data from the current review. If no previous statistics exist for the current product, it creates a new statistics item. ```javascript async function updateStatistics(isRecommended) { let stats = await wixData.get('review-stats', productId); if (stats) { stats.rating += parseInt($w('#radioRating').value, 10); stats.count += 1; stats.recommended += (isRecommended === "true") ? 1 : 0; return wixData.update('review-stats', stats) } stats = { _id: productId, rating: parseInt($w('#radioRating').value, 10), count: 1, recommended: (isRecommended === "true") ? 1 : 0 } return wixData.insert('review-stats', stats) } ``` #### Understanding the Code **Line 2**: Get the review statistics for the current product from the `review-stats` collection. **Line 4**: If statistics data already exist for this product, do the following: **Line 5**: Extract the string representing the value of the radio button rating selected by the user. Use the `parseInt` function to convert the string to an integer. Add the integer to the total rating points. **Line 6**: Increase the ratings count by one. **Line 7**: Check if the reviewer recommended the product. If the `isRecommended` input parameter from the `reviews` collection is true, increase the recommendation count by one. **Line 8**: Update the product's statistics in the `review-stats` collection. **Line 11**: If no previous statistics data exist for this product, create a new statistics item: **LIne 12**: Set the statistics item's ID to the current product's ID. **Line 13**: Extract the string representing the value of the radio button rating selected by the user. Use the `parseInt` function to convert the string to an integer. Set the statistics item's rating to the integer. **Line 14**: Set the statistics item's ratings count to 1 because this is the first rating. **Line 15**: Check if the reviewer recommended the product. If they did, set the recommendation count to 1. **Line 17**: Insert the new product's statistics into the `review-stats` collection. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `review-stats` * `#radioRating` ### Step 13: Create the radioRating\_change Function in the Review Box Popup `radioRating_change` runs when the reviewer chooses a rating. We selected the `radioRating` radio buttons element under "Overall Rating" and, in the Properties & Events panel, added an [`onChange`](https://www.wix.com/velo/reference/$w.RadioButtonGroup.html#onChange) [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). When the visitor chooses a rate, the `radioRating_change` event handler hides the error message displayed when a visitor fails to rate the product. ```javascript export function radioRating_change(event, $w) { $w('#rateError').hide(); } ``` #### Understanding the Code **Lines 1-2**: When the visitor selects a rate, hide the error message stating "Please rate this product". #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#rateError` ### Next Steps * Open [this example](https://editor.wix.com/html/editor/web/renderer/edit/a3686528-4030-475c-b332-721b2548f11a?metaSiteId=d449c6cf-57f8-438b-a284-a0642ca0f7ca) in the Editor to work with the template. * Publish the site and refresh your browser so the Stores collections appear in the Database. * Try other Velo Store examples: * [Adding a Related Products Area to a Wix Store Product Page](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-related-products-area-to-a-wix-store-product-page.md) * [Adding Multiple Items to the Cart in a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-multiple-items-to-the-cart-in-a-wix-stores-site.md) * [Adding a Gift Quiz to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) * [Adding a Product Configurator to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md) * [Adding a Wishlist to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-wishlist-to-a-wix-stores-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding a Related Products Area to a Wix Store Product Page ## Article: Adding a Related Products Area to a Wix Store Product Page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-related-products-area-to-a-wix-store-product-page.md ## Article Content: # Velo Tutorial: Adding a Related Products Area to a Wix Store Product Page > **Note:** This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). This article describes how you can use Velo to add a Related Products area to a Wix Stores Product page. Throughout this article, we're going to use the same site to illustrate the process. You can open the site in the Editor to work with the [template](https://editor.wix.com/html/editor/web/renderer/new?siteId=fac42b60-8aaa-465b-97ef-b193d4042a4c&metaSiteId=23658898-7d95-413f-9e0f-3b21746cb8e9). We're going to explain what we did in the sample site, the code we added to make it work, and give you guidance on how you can modify each step to do something similar on your site.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/06/01/0ba2a67a-527e-4406-8f5c-e3c687416760.png)
### Overview In our example we add a repeater inside a strip that we add to the Stores Product page. This serves as the Related Products area. We define related products in two ways: 1. Products that we manually define as relating to the current product. We create that relationship in a relatedProducts collection. 2. Products whose price is 20% greater or 20% less than the current product. The code then follows the following logic: 1. Get the ID of the currently displayed product. 2. Query the relatedProducts collection and return the products that we defined manually as relating to the currently displayed product.  3. Query the Products collection for items whose price falls within a specific range, relative to the current item's price.  4. Add the results of both queries into an array. 5. First try to display the products that we defined manually as relating to the currently displayed product. If there are none, then display the items whose price falls within a specific range. Display a maximum of four related items. 6. If neither query returns results, hide the repeater. ### Step 1: Site Setup To recreate this functionality you'll need to have Wix Stores added to your site with some products. Once you add Stores to your site, you'll see pages (by clicking on Page Code in the Code sidebar) and collections (by clicking on Databases in the Code sidebar) automatically added to your site.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/25/24df29a9-c9ad-45a2-925d-d194c6ec230d/90c33992-f283-4b9b-8f1d-3d244cc6253a.png)
>**Note** > You may need to save or publish the site and refresh your browser to view the Stores collections in the Databases. #### Database Setup This is what some of the data in our site looks like:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2020/10/26/c2c4e658-1e1d-4417-8bbb-916142879c23/2d2b3f6e-01f7-4c6a-921c-6df5590239fc.png)
In our example we don't work with the Collections collection, although it is in our sample site's store. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2020/10/26/ab2361a0-1ba4-4fa6-b78d-172242c9ccd1/ea98355d-ebb5-43c0-8532-3161471cebc8.png)
The relatedProducts collection has two fields: Product A and Product B, which are both reference fields. For each item, Product A values reference an item in the Products collection. Product B values then reference the product related to the item in Product A. Note that a product may appear in both the Product A or Product B fields. Note also that the field IDs for the two fields are `productA` and `productB`. This is what the relatedProducts collection looks like:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2020/10/26/44d16720-0946-4578-a8ae-726302854f6a/5a64c95b-17c2-4c05-8a63-715cad65d1e2.png)
You can see a copy of the data from the Collections, Products, and relatedProducts collections [here](https://github.com/jeffreya/WixCode-RelatedProducts-data). The Products and Collections collections are created automatically when you have a site with Wix Stores. Because these collections are read-only, you must use the Store Manager to create your product list.  ### Step 2: Setting up the Related Products Area In our example we use a repeater inside a strip as the Related Products area, and we set up the page with the following IDs. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/05/31/b3d1c41f-ad2b-417b-97d4-2f39c874119d.png)
How you can set it up differently To work with the method we use in this example, you'll need somewhere to display the products that relate to the product being displayed on the products page. Repeating elements like repeaters or tables work well, but you can also bind the related products data to other display elements.
### Step 3: Create the loadRelatedProducts Function `loadRelatedProducts` is called from onReady. It gets the current product from the product page and passes that to the functions that find the related products. It then calls the functions to display the results. >**Note:** > Throughout this example we use the new [async/await](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) JavaScript functionality. ```javascript import wixData from 'wix-data'; import wixLocationFrontend from 'wix-location-frontend'; $w.onReady(function () { loadRelatedProducts(); }); async function loadRelatedProducts() { let product = await $w('#productPage').getProduct(); let relatedProductResults = await Promise.all([ relatedProductsByTable(product), relatedProductsByPrice(product) ]); if (relatedProductResults[0].length > 0) showRelatedProducts(relatedProductResults[0]); else showRelatedProducts(relatedProductResults[1]); }; ``` #### Understanding the Code **Line 1-2**: Import the modules we need to work with Wix Data and Wix Location Frontend libraries. **Lines 4-5**: Call `loadRelatedProducts` inside the page's onReady function. `loadRelatedProducts` calls the functions that run the related product queries. It then calls the function to display the query results. Note that we don't return the `loadRelatedProducts` result promise from `$w.onReady`. If we return a promise from `$w.onReady`, it will delay the page load until the promise is resolved. By not returning the promise, we allow the page to load as fast as possible while the related products load in the background. **Line 9**: Get the current product from the Product page. **Lines 10-12**: Create the `relatedProductResults` array from the results of the `relatedProductsByTable` and `relatedProductsByPrice` functions. Each of these functions accepts the current product as a parameter and returns an array of related products. **Lines 15-18**: If the `relatedProductResults` isn't empty, then first try to display the results of `relatedProductsByTable`. If there are none, display the results of `relatedProductsByPrice`. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `productPage`
How you can set it up differently In our example we use two different methods to determine the related products. loadRelatedProducts runs both those methods and prioritizes the relatedProductsByTable results over the relatedProductsByPrice results. You may choose to have a different number of methods for determining the related products and could modify the code accordingly.  Regardless of your methods, you need to follow this basic flow: 1. Get the current product 2. Search for related products 3. Display related products
### Step 4: Create the relatedProductsByTable Function `relatedproductsByTable` finds all the products that relate to the current product, based on the relationships defined in the relatedProducts collection. ```javascript async function relatedProductsByTable(product) { let productId = product._id; // find related products by relation table let relatedByTable = await Promise.all([ wixData.query('relatedProducts') .eq('productA', productId) .include('productB') .find(), wixData.query('relatedProducts') .eq('productB', productId) .include('productA') .find() ]); let relatedProducts = [ ...relatedByTable[0].items.map(_ => _.productB), ...relatedByTable[1].items.map(_ => _.productA) ]; return relatedProducts; }; ``` #### Understanding the Code **Lines 5-14**: Run two parallel queries on the `relatedProducts` collection. Each query returns the value in the productA or productB fields that relate to the currently displayed product. Use `.include` to include all the data from the referenced field for the related product. **Lines 16-19**: Use the spread operator and map function to build an array of related products from both fields. The spread operator flattens the results of both queries into one array. The map function extracts only the related product from the item object of the query results while removing the current product. Note that the \_ is a variable that represents the current element being processed in the array. #### Items you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `relatedProducts` * `productA` * `productB`
How you can set it up differently You can create any criteria you want for defining related products. If you choose to change the logic of this function, you will need to make sure that the relatedProductsByTable function returns the items object of the results of a wixData query.
### Step 5: Create the relatedProductsByPrice Function `relatedproductsByPrice` finds all the products that relate to the current product, based on a price range. ```javascript async function relatedProductsByPrice(product) { let productId = product._id; // find related products by price let relatedByPrice = await wixData.query('Stores/Products') .between('price', product.price * 0.8, product.price * 1.2) .ne('_id', productId) .find(); return relatedByPrice.items; }; ``` #### Understanding the Code **Lines 5-8**: Run a query on the Products collection for any products whose price is within 20% of the price of the currently displayed product. Use `.ne` to exclude the current product from the results. **Line 9**: Return the `.items` object of the query results.
How you can set it up differently You can create any criteria you want for defining related products. If you choose to change the logic of this function, you will need to make sure that the relatedProductsByPrice function returns the items object of the results of a wixData query.
### Step 6: Create the showRelatedProducts Function `showrelatedProducts` accepts the results of `relatedProductsByTable` or `relatedProductsByPrice` and truncates the results so only a maximum of four items are displayed. It then sets the [`onItemReady`](https://www.wix.com/code/reference/$w.Repeater.html#onItemReady) function for the repeater, defines the repeater data, and decide whether to hide or display the repeater. ```javascript function showRelatedProducts(relatedProducts) { if (relatedProducts.length > 0) { relatedProducts.splice(4, relatedProducts.length); $w('#relatedItemsRepeater').onItemReady(relatedItemReady); $w("#relatedItemsRepeater").data = relatedProducts; $w("#relatedItems").expand(); } else { $w("#relatedItems").collapse(); } }; ``` #### Understanding the Code **Line 2**: Check if `relatedProducts` contains data. If it does, run lines 3-6. **Line 3**: Remove all but the first four results from `relatedProducts`. **Line 4**: Set the `onItemReady` function for the repeater to `relatedItemReady`. We'll define that function in the next step. **Line 5**: Set the `data` property for the repeater. **Line 6**: Expand the repeater. **Line 7-8**: If the `relatedProducts` does not contains data, collapse it. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `relatedItemsRepeater` * `relatedItems`
How you can set it up differently To work with the method we use in this example, you'll need somewhere to display the related products. Repeating elements like repeaters or tables work well, but you can also bind the related products data to other display elements. It's a good idea to check if there are related products, and if not, prevent those elements from appearing.
### Step 7: Create the relatedItemReady Function `relatedItemReady` is the [onItemReady](https://www.wix.com/code/reference/$w.Repeater.html#onItemReady) function for the repeater. It's the function that runs when a new repeated item is created. ```javascript function relatedItemReady($w, product) { $w("#productImage").src = product.mainMedia; $w("#productName").text = product.name; $w("#productPrice").text = product.formattedPrice; $w('#productImage').onClick(() => { wixLocationFrontend.to(product.productPageUrl); }); }; ``` #### Understanding the Code **Lines 2-4**: Bind each element in the repeater to the corresponding data for the product. **Lines 5-6**: Set the `onClick` property of the `productImage` element to open the product page for the specific product. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `productImage` * `productName` * `productPrice` * `productImage`
How you can set it up differently Depending on how you choose to display the related products, you'll need to set up those elements to display the product data. It's a good idea to make at least one element something your users can click that points to the product page for the related product.
### Next Steps * Open [this example](https://editor.wix.com/html/editor/web/renderer/new?siteId=fac42b60-8aaa-465b-97ef-b193d4042a4c&metaSiteId=23658898-7d95-413f-9e0f-3b21746cb8e9) in the Editor to work with the template. * Publish the site and refresh your browser so the Stores collections appear in the Database. * Try other Velo Store examples: * [Adding a Wishlist to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-wishlist-to-a-wix-stores-site.md) * [Adding Multiple Items to the Cart in a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-multiple-items-to-the-cart-in-a-wix-stores-site.md) * [Adding a Gift Quiz to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) * [Adding a Product Configurator to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md) * [Adding Ratings and Reviews to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding Multiple Items to the Cart in a Wix Stores Site ## Article: Adding Multiple Items to the Cart in a Wix Stores Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-multiple-items-to-the-cart-in-a-wix-stores-site.md ## Article Content: # Velo Tutorial: Adding Multiple Items to the Cart in a Wix Stores Site >**Note:** > This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). This article describes how you can use Velo to add multiple items at once to the shopping cart in a Wix Stores site. Throughout this article, we're going to use [this site](https://www.wix.com/code-examples/bulk-add-to-cart-vw) to illustrate the process. You can follow along by opening the site [in the Wix Editor](https://editor.wix.com/html/editor/web/renderer/new?siteId=11e2a942-7c54-4bbb-9ffb-cce15e010fb1&metaSiteId=635e3b99-e23b-428e-8947-0fd14be46ddc&autoDevMode=true). We're going to explain how we set up the sample site and the code we added to make it work.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/e7babbdc-7174-402a-909c-2ea03039e9c8.png)
### Overview Our site allows visitors to create a custom charm bracelet. Visitors choose the material of the bracelet and 5 charms to add to it. When the site visitor finishes the customization process, the bracelet—along with all of the chosen charms—are added to the shopping cart.  Our site contains a Wix Store with all the different bracelet and charm types. **We also added a Charm Bracelet page which contains:** * An image of a charm bracelet with five overlaid images for charms that are added to the bracelet. The overlaid images are hidden on load. * Three collapsible sections implemented using container boxes. These sections represent the three steps a site visitor is required to complete before purchasing a custom bracelet. Steps 1 and 2 contain a button to move to the next section, and step 3 contains a button to add the items to the cart. Site visitors can also move between sections by clicking on the text elements that serve as the section headers.  * The first section contains three buttons for choosing the bracelet material. * The second section contains a repeater that shows the available and selected charms. * The third section contains a text element that shows which material bracelet was chosen, a repeater that shows which charms were chosen, and another text element that shows the total price. * A shopping cart.  **Then we added code to do the following:** * Set up the page's navigation so site visitors can easily switch between sections. * Get the product information for the bracelet and all the charms. * Add repeater data and logic for selecting and displaying bracelet charms. * Calculate the price of the selected bracelet and charms, and add them to the store's shopping cart. ### Step 1: Set Up the Site To recreate this functionality, you'll need to have Wix Stores added to your site with some products. In our site, the products are the different bracelet and charm types. Once you add Stores to your site, you'll see pages and collections automatically added to your site.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/9eead11a-f966-496b-9ce1-b894241c64b3/4447e177-1051-43f3-9f81-7a09abf1e757.jpg)
>**Notes:** > * In this example we don't work with the Collections collection, although it is in our sample site's store.  > * You may need to save or publish the site and refresh your browser to view the Stores collections in the Database. This is what some of the data in our site looks like:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/3bb22b8c-26be-433f-a443-2af5feddcc86/8716af1f-c618-4d68-ad62-5cd3ae2558a0.png)
### Step 2: Set Up the Charm Bracelet Page Here's everything we added on the Charm Bracelet page: * A dataset connected to the Products collection. * A shopping cart. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/09/03/944df7ca-2d4b-478f-a743-8eb7dd176fd5.png) * Images for the bracelet and charms. The bracelet image changes based on the chosen material. The charmPlace images change based on the selected charms.   ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/4d766cde-97d6-4ddb-9610-0ecdfe7d3ab6.png) * Some text elements for displaying explanatory information. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/644c3ae0-c20a-468f-a8f4-581ddad0e58e.png) * Three buttons styled to look like section headings. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/624554ed-8a03-4232-bda3-3d97e67c153a.png) * Three container boxes for the three steps in the customization process. Elements are grouped in a container boxes so that they can easily be collapsed and expanded together. * The first container box contains buttons for choosing the bracelet material, and a button for moving to the next step. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/bbe02a71-d69f-4cff-9872-51131f131f99.png) * The second container box contains a repeater for choosing the bracelet charms, and a button for moving to the next step. Each repeater item consists of three layers: * A charm overlay for indicating whether the charm is still available for selection. * A charm image that shows the actual charm. * A charm border for indicating whether the charm is selected. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/47436fff-2773-4133-ad57-97f9012babea.png) * The third container box contains elements for reviewing the customized bracelet and adding the items to the shopping cart. * A text element to show the material of the selected bracelet. * A repeater to show the selected charms. * A text element to show the price of the selected bracelet and charms. * A button to add all the selected items to the cart. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/08/23/2f435bfe-d0d1-4908-9550-6d72089a0636.png) ### Step 3: Create an Import and Global Variables All of the code in this site is added to the Charm Bracelet page.  The code begins with an import statement and some variable declarations. ```javascript import wixData from 'wix-data'; const MAX_NUMBER_OF_CHARMS = 5; const selectedCharms = []; let braceletsMap = {}; let selectedBracelet; ``` **Line 1**: Import the `wix-data` module, which is used to query the site's collections. **Line 3**: Create a constant that determines the maximum number of charms that can be added to the bracelet. **Line 4**: Create an array that will contain the charms that are selected by the site visitor. **Line 5**: Create an object that will contain the product information for the various types of bracelets. **Line 6**: Create a variable to store the type of bracelet selected. ### Step 4: Create the onReady Function When the page loads, get the product information for the various types of bracelets and store it in a convenient way to use later. >**Note:** > Throughout this example we use the [async/await](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) JavaScript functionality. ```javascript $w.onReady(async () => { const braceletNames = ['Silver Bracelet', 'Rose Gold Bracelet', 'Gold Bracelet']; let newQuery = await wixData.query("Stores/Products") .hasSome("name", braceletNames).find(); const allBracelets = newQuery.items; allBracelets.forEach(product => { const keyName = product.name.split(' ')[0].toLowerCase(); braceletsMap[keyName] = product; }); }); ``` **Line 1**: Declare the `onReady()` event handler. **Line 2**: Create an array of all the available bracelet types. **Line 3-4**: Query the Products collection to find the products that match the bracelet names from the list created in line 2. **Line 5**: Get the items returned in the query results. **Line 6-8**: For each item returned by the query, create a key in the `braceletsMap` object corresponding to the bracelet type (e.g., the key for `'Silver Bracelet'` is set as `silver`). Then set the value for that key to the product information retrieved for that type of bracelet. ### Step 5: Create onClick Functions for Each Header Each step in the customization process has a header. The header is actually a button that site visitors can click to expand the relevant section and collapse all other sections. ```javascript export function selectBraceletText_click(event, $w) { $w('#braceletSelectionStrip').expand(); $w('#charmsSelectionStrip').collapse(); $w('#addToCartStrip').collapse(); }; export function chooseCharmsText_click(event, $w) { $w('#braceletSelectionStrip').collapse(); $w('#charmsSelectionStrip').expand(); $w('#addToCartStrip').collapse(); }; export function reviewBraceletText_click(event, $w) { $w('#braceletSelectionStrip').collapse(); $w('#charmsSelectionStrip').collapse(); $w('#addToCartStrip').expand(); }; ``` **Lines 1-5, 7-11, 13-17**: When a header button is clicked, expand the corresponding section and collapse all others. ### Step 6: Create onClick Functions for Each Material Button In step 1 of the customization process, site visitors choose the material of the charm bracelet using one of three buttons. The buttons are wired to similar `onClick` event handlers. ```javascript export function gold_click(event, $w) { selectBracelet(braceletsMap.gold); }; export function silver_click(event, $w) { selectBracelet(braceletsMap.silver); }; export function rosegold_click(event, $w) { selectBracelet(braceletsMap.rose); }; ``` **Lines 1-3, 5-7, 9-11**: The button event handlers call the `selectBracelet()` function, passing the product information corresponding to the chosen material. The `selectBracelet()` function performs all the necessary changes to the page's elements when the chosen material of the bracelet changes and is described in detail in step 7 of this article. ### Step 7: Create the selectBracelet Function The `selectBracelet()` function receives the information from the Products collection corresponding to the chosen bracelet material. ```javascript function selectBracelet(bracelet) { selectedBracelet = bracelet; $w('#bracelet').src = bracelet.mainMedia; $w('#nextButton').enable(); $w('#chooseCharmsText').enable(); $w('#selectedBracelet').text = bracelet.name + " + these charms:"; $w('#selectedBracelet').show(); }; ``` **Line 2**: Set the `selectedBracelet` global variable to the product information of the chosen bracelet. **Line 3**: Set the bracelet image to the main image from the chosen bracelet's product information. **Line 4**: Enable the button used to move to the next step of the customization process. See step 8 of this article for a description of the code that runs when this button is clicked. **Line 5**: Enable the heading used to move to the next step of the customization process. See step 5 of this article for a description of the code that runs when this button is clicked. **Line 6**: Set the review text in step 3 of the customization process to reflect the selected bracelet type. **Line 7**: Show the review text in step 3 of the customization process. ### Step 8: Create the nextButton\_click Function The `nextButton_click()` function is wired to the next button in step 1 of the customization process. The button is used to move to step 2. ```javascript export function nextButton_click(event, $w) { $w('#braceletSelectionStrip').collapse(); $w('#charmsSelectionStrip').expand(); $w('#chooseCharmsText').enable(); } ``` **Line 2**: Collapse the first section. **Line 3**: Expand the second section. **Line 4**: Enable the header button of the second section. ### Step 9: Create the charmsRepeater\_itemReady Function The `charmsRepeater_itemReady()` function is wired to the repeater used to select charms in step 2 of the customization process. The function is called for each repeater item as it is created. It sets what happens when a specific charm is clicked. ```javascript export function charmsRepeater_itemReady($w, itemData, index) { $w('#charmImage').onClick(() => { if ($w('#charmBorder').hidden) { selectedCharms.push(itemData); $w('#charmBorder').show(); showCharmOnBracelet(itemData.mainMedia); $w('#selectedCharmsRepeater').data = selectedCharms; if (selectedCharms.length === MAX_NUMBER_OF_CHARMS) { disableCharmsSelection(); } } else { if (selectedCharms.length === MAX_NUMBER_OF_CHARMS) { enableCharmsSelection(); } const indexOfCharm = selectedCharms.findIndex(item => item._id === itemData._id); selectedCharms.splice(indexOfCharm, 1); $w('#selectedCharmsRepeater').data = selectedCharms; hideCharmFromBracelet(itemData.mainMedia); $w('#charmBorder').hide(); } }); }; ``` **Line 2**: Create an `onClick` event handler on the charm image. **Line 3**: If the item's border is hidden, meaning the charm is not currently selected: **Line 4**: Add the clicked charm to the list of selected charms. **Line 5**: Show the border to indicate that the charm is now selected. **Line 6**: Call the `showCharmOnBracelet()` function with the selected charms image. The `showCharmOnBracelet()` function adds the specified charm image to the next open spot on the bracelet image, as described in step 10 of this article. **Line 7**: Reset the repeater in step 3 of the customization process to reflect the newly selected charm. **Lines 8-10**: If the selection means the maximum number of charms has been reached, disable further charm selection by calling the `disableCharmsSelection()` function described in step 11 of this article. **Line 11**: If the item's border is shown, meaning the charm is currently selected: **Lines 12-14**: If the deselection means the maximum number of charms is no longer selected, enable further charm selection by calling the `enableCharmsSelection()` function described in step 11 of this article. **Lines 15-16**: Find the index of the charm that is being deselected and remove it from the list of selected charms. **Line 17**: Reset the repeater in step 3 of the customization process to reflect the newly deselected charm. **Line 18**: Call the `hideCharmFromBracelet()` function with the selected charms image. The `hideCharmFromBracelet()` function removes the specified charm image from the bracelet image, as described in step 10 of this article. **Line 19**: Hide the border to indicate that the charm is no longer selected. ### Step 10: Create the showCharmOnBracelet and hideCharmFromBracelet Functions The `showCharmOnBracelet()` and `hideCharmFromBracelet()` functions are used to add or remove charms from the bracelet image when they are selected in step 2 of the customization process. ```javascript function showCharmOnBracelet(charmImage) { for (let i = 1; i <= 5; i++) { if ($w(`#charmPlace${i}`).hidden) { $w(`#charmPlace${i}`).src = charmImage; $w(`#charmPlace${i}`).show(); break; } } }; function hideCharmFromBracelet(charmImage) { for (let i = 1; i <= 5; i++) { if ($w(`#charmPlace${i}`).src === charmImage) { $w(`#charmPlace${i}`).src = 'http://'; $w(`#charmPlace${i}`).hide(); break; } } }; ``` **Lines 2-8**: Find the first charm place with a hidden charm image, set its image to be the passed in image, and then show the charm image. **Lines 12-18**: Find the charm place that matches the passed in image, set its image source to a blank image, and then hide the charm image. ### Step 11: Create the disableCharmsSelection and enableCharmsSelection Functions The `disableCharmsSelection()` and `enableCharmsSelection()` functions are used to disable or enable the selection of charms in step 2 of the customization process. ```javascript function disableCharmsSelection() { $w('#charmsRepeater').forEachItem(($w, itemData) => { if (!isCharmSelected(itemData)) { $w('#charmOverlay').show(); } }); }; function enableCharmsSelection() { $w('#charmsRepeater').forEachItem($w => { $w('#charmOverlay').hide(); }); }; ``` **Lines 2-6**: For each item in the charms repeater, if the charm isn't selected, show the overlay, which stops the item from being selected. To determine if the charm is selected, the `isCharmSelected()` function is called. The `isCharmSelected()` function is described in step 12 of this article. **Lines 9-12**: For each item in the charms repeater, hide the overlay, which allows the charms to be selected. ### Step 12: Create the isCharmSelected Function The `isCharmSelected()` function checks to see if a specific charm is selected. ```javascript function isCharmSelected(charmItem) { return selectedCharms.findIndex(item => item._id === charmItem._id) > -1; }; ``` **Line 2**: Use the JavaScript `findIndex()` function to check if the specified charm exists in the list of selected charms. ### Step 13: Create the selectedCharmsRepeater\_itemReady Function The `selectedCharmsRepeater_itemReady()` function is wired to the repeater used to display the selected charms in step 3 of the customization process. The function is called for each repeater item as it is created. ```javascript export function selectedCharmsRepeater_itemReady($w, itemData, index) { $w('#selectedCharmImage').src = itemData.mainMedia; }; ``` **Line 2**: Set the item's image to the specified image from the repeater's data. ### Step 14: Create the finishButton\_click Function The `finishButton_click()` function is wired to the finish button in step 2 of the customization process. The button is used to move to step 3 of the process. ```javascript export function finishButton_click(event, $w) { $w('#braceletSelectionStrip').collapse(); $w('#charmsSelectionStrip').collapse(); calcAndShowPrice(); $w('#addToCartStrip').expand(); $w('#reviewBraceletText').enable(); $w('#addToCartButton').enable(); }; ``` **Line 2-3**: Collapse sections one and two. **Line 4**: Calculate the price of the bracelet and charms to be displayed in section three using the `calcAndShowPrice()` function. The `calcAndShowPrice()` function is described in step 15 of this article. **Line 5**: Expand the third section. **Line 6**: Enable the heading used to move to step 3 of the customization process. See step 5 of this article for a description of the code that runs when this button is clicked. **Line 7**: Enable the button that adds the bracelet and selected charms to the store's shopping cart. See step 16 of this article for a description of the code that runs when this button is clicked. ### Step 15: Create the calcAndShowPrice Function The `calcAndShowPrice()` function calculates the price of the selected bracelet and charms. It then shows the price in the third section. ```javascript function calcAndShowPrice() { let price = selectedBracelet.price; selectedCharms.forEach(charm => { price += charm.price; }); $w('#price').text = price.toString(); }; ``` **Line 2**: Get the price of the selected bracelet. **Lines 3-5**: For each selected charm, add its price to the total price. **Line 6**: Show the calculated price in the third section. ### Step 16: Create the addToCartButton\_click Function The `addToCartButton_click()` function is wired to the add to cart button in step 3 of the customization process. The button is used to add the selected bracelet and charms to the store's shopping cart. ```javascript export async function addToCartButton_click(event, $w) { let itemsToAdd = [{ productID: selectedBracelet._id, quantity: 1, options: {} }]; selectedCharms.forEach(function (charm) { itemsToAdd.push({ productID: charm._id, quantity: 1, options: {} }); }); let res = await $w('#shoppingCartIcon').addProductsToCart(itemsToAdd); }; ``` **Lines 2-4**: Define the bracelet and charms to be added to the shopping cart. **Line 6**: Add the bracelet and selected charms all at once to the store's shopping cart. ### Next Steps * Open [this example](http://editor.wix.com/html/editor/web/renderer/new?siteId=11e2a942-7c54-4bbb-9ffb-cce15e010fb1&metaSiteId=635e3b99-e23b-428e-8947-0fd14be46ddc) in the Editor to work with the template. * Publish the site and refresh your browser so the Stores collections appear in the Database. * Try other Velo Stores examples: * [Adding a Related Products Area to a Wix Store Product Page](https://support.wix.com/en/article/adding-a-related-products-area-to-a-wix-store-product-page) * [Adding a Wishlist to a Wix Stores Site](https://support.wix.com/en/article/adding-a-wishlist-to-a-wix-stores-site) * [Adding a Product Configurator to a Wix Stores Site](https://support.wix.com/en/article/adding-a-product-configurator-to-a-wix-stores-site) * [Adding Ratings and Reviews to a Wix Stores Site](https://support.wix.com/en/article/adding-ratings-and-reviews-to-a-wix-stores-site) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding a Product Configurator to a Wix Stores Site ## Article: Adding a Product Configurator to a Wix Stores Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md ## Article Content: # Velo Tutorial: Adding a Product Configurator to a Wix Stores Site > **Note:** This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). This article describes how you can use Velo to add a product configurator to a Wix Stores site. The product configurator allows site visitors to choose different patterns and colors to design a custom sweater. Throughout this article we're going to use the same site to illustrate the process. You can open the site in the Editor to work with the [template](https://editor.wix.com/html/editor/web/renderer/new?siteId=9c0ebabc-9cbc-4bf8-bf52-c9170f6a8524&metaSiteId=ba59d309-8678-451d-a4bc-2c8ba968e452&autoDevMode=true). We're going to explain how we set up the sample site and the code we added to make it work.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/08/27/6509a260-ea37-422a-8fbb-764bcba8eda7.png)
### Overview **In our site we added the following:** * A Sweater page including: * A default sweater image and overlaid images for the body, sleeves, pocket, and neckline.  * Elements to display the name, price, and description. * Sections for selecting the pattern or color for each sweater part. Each section has a collapsible container with a repeater containing buttons for selecting a pattern or color, plus a header button for expanding and collapsing each container. * A combinedMaterialsImages collection. The collection has an item (row) for each pattern or color option available for each sweater part. The items includes images for displaying the pattern or color. **Then we added code to do the following:** * When the Sweater page loads, clear sweater customization settings. * When a visitor clicks a header button, expand the container holding the repeater, displaying buttons with patterns or colors for the selection. Collapse containers in other sections. * When a visitor clicks a button, store the visitor's choice in an object. * When all selections are made, enable the Add to Cart button. When the visitor clicks the button, add the customized product to the shopping cart. ### Step 1: Set Up the Site To recreate this functionality, you'll need to have Wix Stores added to your site with at least one product. Once you add Stores to your site, you'll see pages and collections automatically added to your site.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/6da6f237-6b81-4850-a27b-74fc75a5c4c4/35207879-0d8b-4cf4-bf9f-7636d67a92e2.jpg)
The Products and Collections collections are created automatically when you have a site with Wix Stores. Because these collections are read-only, you must use the Store Manager to create your product list. In this example we don't work with the Collections collection, although it is in our sample site's store. >**Note** > You may need to save or publish the site and refresh your browser to view the Stores collections in the Database. This example is based on the Patchwork Sweater product, one of the products in the Products collection.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/9b191748-9992-49ba-b96c-3b3208227f74/975218c8-fe98-4174-b4a0-7d956d79a906.png)
We also added a Sweater page and a combinedMaterialsImages collection to our database. We'll describe the Sweater page's setup later in this article.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/dc2a31c6-eb2b-4357-9571-1708b6c92f88/707e0e23-fbf0-4264-bec6-9607dcd4e9df.jpg)
The combinedMaterialsImages collection has four fields: * Title: The name of the pattern or color for the sweater part * option: The part of the sweater that the pattern or color modifies * choiceImage: A sample image of the pattern or color * displayImage: An image of the sweater part with the specific pattern or color
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/10/26/4d006738-20d6-4e39-aa3d-d1b6d1016e4b/df7eb8ba-013c-4a2d-b7e1-82e6afef8243.png)
The choiceImage images are used to display a sample of the pattern or color for each sweater part and the displayImage images display the selected pattern/color in the sweater picture. ### Step 2: Set Up the Sweater Page On the Sweater page we added: * A strip to keep page elements neat and organized. * A default image of the sweater, overlaid by images of each sweater part. The default sweater image looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/08/29/b13c02c4-d244-46a0-8d4d-e0b37ea55b95.png)
The individual parts of the sweater look like this: 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/08/29/539a95d0-8e49-4990-8c99-bf7e78fcb602.png)
* We also added a refresh button to reset to the default sweater image and restart the sweater customization process. Note that when the page is loaded or the refresh button is clicked, the individual sweater part images are hidden and the default sweater image is displayed.  * The sweater's title, price, and description from the Products collection. * A container for customizing each sweater part, which collapses and expands.  * A repeater inside each container with buttons for selecting the sweater part color or pattern. When the visitor hovers over a button, a black border appears (the hover effect is added using [Button Design](https://support.wix.com/en/article/wix-editor-customizing-the-design-of-your-buttons) without code). Clicking a button selects a pattern or color for the sweater part. * A header button above each repeater with the name of the sweater part, and up and down arrows. Clicking the button expands the container holding the repeater for that sweater part, and collapses the containers for other sweater parts. > **Note**: In the image below, the two arrows are displayed separately. On the actual site, they're overlapping and the code controls which one is displayed. * Five datasets: * A Products collection dataset for displaying the title, price, and description of the product.  * Four filtered datasets connected to the combinedMaterialsImages collection, one for each sweater part. The datasets are filtered by option (body, sleeves, pocket, neckline). Each filtered dataset is connected to the buttons in the corresponding repeater. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/08/29/ff6cf4ea-7784-42fe-91a1-477511cf9f6f.png) * An Add to Cart button for adding the customized sweater to the visitor's cart. The Add to Cart button is disabled and appears gray by default. After selecting patterns or colors for all sweater parts, the button is enabled and appears black. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/08/28/487ca509-e4fa-4356-835b-712718de4b1f.png) * A shopping cart icon that displays the number of products added to the cart. Clicking the cart brings the visitor to the Cart page, where they can check out. ### Step 3: Prepare the Sweater Page On the Sweater page we start by importing the module we need to work with the [Wix Window Frontend](https://www.wix.com/code/reference/wix-window.html). We use this module to check whether the visitor is on a desktop or mobile device. This code needs to be added at the top of the code editor. Then we declare global variables to be used throughout the code. Finally, we call the `clearSelection` function to clear customization settings and return the sweater to its default state. >**Note** > All code for this example is located on the Sweater page. ```javascript import wixWindowFrontend from 'wix-window-frontend'; import { cart } from 'wix-stores-frontend'; const NUMBER_OF_CHOICES = 4; const productId = "ad8e5eee-5939-d0aa-6094-e620986543fe"; let selectedOptions = {}; $w.onReady(function () { clearSelection(); }); ``` #### Understanding the Code **Line 1**: Import the module we need to work with the Wix Window Frontend library. **Line 2**: Import the module we need to work with the Wix Stores Cart API in [Step 11](https://support.wix.com/en/article/velo-tutorial-adding-a-product-configurator-to-a-wix-stores-site#step-11-create-the-addtocartbutton_click-function). **Lines 3-5**: Define the following variables: * `NUMBER_OF_CHOICES`: Number of sweater parts that can be customized * `productId`: Hard-coded product ID of the sweater from the Products collection * `selectedOptions`: An object that stores the selected pattern or color for each sweater part **Lines 7-8**: When the page loads, call the `clearSelection` function to clear customization settings. There are no identifiers you would need to change here to make this code work on your site. ### Step 4: Create the refreshButton\_click Function When the refresh button on the sweater image is clicked, the `refreshButton_click` [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) clears sweater customization settings by calling the `clearSelection` function. ```javascript export function refreshButton_click(event, $w) { clearSelection(); }; ``` #### Understanding the Code **Lines 1-2**: When the refresh button is clicked, run the `clearSelection` function to clear sweater customization settings. There are no identifiers you would need to change here to make this code work on your site. ### Step 5: Create the clearSelection Function The `clearSelection` function clears all the customized selections so the sweater reverts back to the default setting. `clearSelection` runs when the Sweater page loads and when a visitor clicks the refresh button on the sweater image. ```javascript function clearSelection() { selectedOptions = {}; $w('#bodyImg').hide(); $w("#sleevesImg").hide(); $w('#necklineImg').hide(); $w("#pocketImg").hide(); $w("#bodyImg").src = 'https://'; $w("#sleevesImg").src = 'https://'; $w("#pocketImg").src = 'https://'; $w("#necklineImg").src = 'https://'; $w("#addToCartButton").disable(); }; ``` #### Understanding the Code **Line 2**: Clear the `selectedOptions` object, which contains the visitor's pattern or color selections for each sweater part. **Lines 3-6**: Hide the overlaid sweater part images so that only the default sweater is displayed. **Lines 7-10**: Clear the sweater part images. **Line 11**: Disable the Add to Cart button. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#bodyImg` * `#sleevesImg` * `#necklineImg` * `#pocketImg` * `#addToCartButton` ### Step 6: Create the bodySelectionRepeater\_itemReady Function >**Note** > In this section, we use the "body" part of the sweater as an example. The code is the same for the other sweater parts, except for the following: > > * The word "body" is replaced with "sleeves", "pocket", or "neckline". > * The toggleFold parameter (1) is replaced by the corresponding index number (2, 3, and 4) for each sweater part. We selected the repeater element and, in the Properties & Events panel, added an [`onItemReady`](https://www.wix.com/code/reference/$w.Repeater.html#onItemReady) [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md). The `bodyDataset` filters the `combinedMaterialsImages` collection to include only items in the collection whose `option` field is `body`. Each filtered item (row) in the collection is passed to the `bodySelectionRepeater_itemReady` event handler as the `itemData` parameter. After the page loads and the filtered dataset connects to the repeater, `bodySelectionRepeater_itemReady` loads each pattern/color image associated with "body" items into a button in the repeater. When the visitor clicks a button in the repeater, an `onClick` event handler runs the `selectChoiceForOption` function. If the visitor is using a mobile device, when they click a button, the `toggleFold` function runs, collapsing the container holding the repeater. ```javascript export function bodySelectionRepeater_itemReady($w, itemData, index) { $w('#selectBodyButton').onClick(() => { selectChoiceForOption($w, 'body', itemData); if (wixWindowFrontend.formFactor === 'Mobile') { toggleFold(1); } }); }; ``` #### Understanding the Code **Line 1**: Set up each item in the `bodySelectionRepeater` as it's loaded. **Line 2**: Set the action that occurs when a visitor clicks a choice for the body option. **Line 3**: When a button is clicked, run the `selectChoiceForOption` function for the body sweater part with the selected `itemData` from the collection. **Lines 4-5**: Check whether the site is being viewed on a mobile device. If it is, run `toggleFold` with the index number associated with the body sweater part to collapse the container holding the repeater. #### Identifiers you may need to change based on your site's collections If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#selectBodyButton` * `body` ### Step 7: Create the selectChoiceForOption Function The `selectChoiceForOption` function runs when a visitor selects a particular pattern or color for a sweater part. It adds the selection to the `selectedOptions` object, displays the selected pattern or color on the sweater part, and when all sweater part selections are finished, enables the Add to Cart button. ```javascript function selectChoiceForOption($w, option, choiceData) { selectedOptions[capitalizeFirstLetter(option)] = choiceData.title; $w(`#${option}Img`).src = choiceData.displayImage; $w(`#${option}Img`).show(); if (Object.keys(selectedOptions).length === NUMBER_OF_CHOICES) { $w('#addToCartButton').enable(); }; }; ``` #### Understanding the Code **Line 1**: Run the function with the following parameters: * `$w`: Global selector * `option`: The current sweater part (e.g., body) * `choiceData`: The item from the combinedMaterialsImages collection that was selected. **Line 2**: Add the name of the pattern/color for the selected choice to the `selectedOptions` object (e.g., `Body: Stripe Pattern`). Since the sweater part options are capitalized in the Product collection, we first capitalize the option key. **Line 3**: Use ${expression} inside a template literal (string indicated by backticks) to extract the name of the sweater part image from the `option` parameter. Change the image to the selected choice's `displayImage` from the `combinedMaterialsImages` collection. **Line 4**: Show the selected image in the sweater. **Line 5**: Check if the `selectedOptions` object contains selections for all sweater parts. This is done by comparing the length of the object (using the `Object.keys` function) to the number of choices. **Line 6**: If all sweater part selections were made, enable the `addToCartButton` button. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#${option}Img` * `#addToCartButton` ### Step 8: Create the capitalizeFirstLetter Function The `capitalizeFirstLetter` function capitalizes the first letter of the string passed to the function. ```javascript function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); }; ``` #### Understanding the Code **Line 2**: Capitalize the first letter of the input string. This is done by using the `chartAt` function to return the first letter of the string, using `toUpperCase` to capitalize the letter, and using `slice` to remove the second (lowercase) letter. There are no identifiers you would need to change here to make this code work on your site. ### Step 9: Create the option1Button\_click Function >**Note** > In this section, we use the "body" part of the sweater as an example. The code is the same for the other sweater parts, except for the following: > > * Replace the number "1" in option**1**button\_click by the corresponding option number (2, 3, and 4). > * Replace the number "1" by the corresponding option number (2, 3, and 4) for the parameter passed to the toggleFold function. `option1Button_click` is an `onClick` event handler that calls the `toggleFold` function when the button above the repeater is clicked. We explain `toggleFold` in [Step 10](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md). ```javascript export function option1Button_click(event, $w) { toggleFold(1); }; ``` #### Understanding the Code **Line 2**: Call the `toggleFold` function with an index of "1". There are no identifiers you would need to change here to make this code work on your site. ### Step 10: Create the toggleFold Function `toggleFold` expands or collapses the container under the button clicked by the visitor. If the container is currently collapsed, the function expands the container. If the container is currently expanded, the function collapses the container. `toggleFold` also collapses all other sweater part containers. ```javascript function toggleFold(index) { let $fold = $w(`#option${index}Box`); let $arrowDown = $w(`#arrowDown${index}`); let $arrowUp = $w(`#arrowUp${index}`); if ($fold.collapsed) { $fold.expand(); $arrowDown.show(); $arrowUp.hide(); } else { $fold.collapse(); $arrowDown.hide(); $arrowUp.show(); } for (let i = 1; i <= NUMBER_OF_CHOICES; i++) { if (i !== index) { $w(`#option${i}Box`).collapse(); $w(`#arrowDown${i}`).hide(); $w(`#arrowUp${i}`).show(); }; }; }; ``` #### Understanding the Code **Lines 2-4**: Define identifiers for the current sweater part's container, up arrow, and down arrow. **Lines 5-8**: If the current container is collapsed, expand it, hide the up arrow, and show the down arrow. **Lines 9-13**: If the current container is expanded, collapse it, hide the down arrow, and show the up arrow. **Line 14**: Collapse all other containers. For each sweater part index: **Line 15-18**: If the index is not associated with the current sweater part, collapse the container, hide the down arrow, and show the up arrow. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `#option1Box` * `#arrowUp` * `#arrowDown` ### Step 11: Create the addToCartButton\_click Function `addToCartButton` is an [event handler](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) that runs when the Add to Cart button is clicked. It runs the Wix Stores Frontend API [`cart.addProducts`](https://www.wix.com/velo/reference/wix-stores/cart/addproducts) function, adding the customized product to the cart. ```javascript // Start with clean slate when page loads. $w.onReady(function () { $w('#addToCartButton').onClick((event) => { cart.addProducts([{ "productId": productId, "quantity": 1, "options": { choices: selectedOptions } }]) }) }); ``` #### Understanding the Code **Line 2**: Run the `addToCart` function on the shopping cart with the following inputs: * The sweater's product ID * Quantity of customized sweaters to add to cart * Sweater options selected by the visitor contained in the `selectedOptions` object #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `shoppingCartIcon1` ### Next Steps * Open [this example](http://editor.wix.com/html/editor/web/renderer/new?siteId=7dfed114-5619-42da-9a30-9a95a6428270&metaSiteId=2cbc3f84-7b5a-4511-81af-a97fd016cba0) in the Editor to work with the template. * Publish the site and refresh your browser so the Stores collections appear in the Database. * Try other Velo Store examples: * [Adding a Related Products Area to a Wix Store Product Page](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-related-products-area-to-a-wix-store-product-page.md) * [Adding a Wishlist to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-wishlist-to-a-wix-stores-site.md) * [Adding Multiple Items to the Cart in a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-multiple-items-to-the-cart-in-a-wix-stores-site.md) * [Adding a Product Configurator to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-a-product-configurator-to-a-wix-stores-site.md) * [Adding Ratings and Reviews to a Wix Stores Site](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/adding-ratings-and-reviews-to-a-wix-stores-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Cross-Selling a Product in Your Store with Velo by Wix ## Article: Cross-Selling a Product in Your Store with Velo by Wix ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/cross-selling-a-product-in-your-store-with-velo-by-wix.md ## Article Content: # Velo Tutorial: Cross-Selling a Product in Your Store with Velo by Wix > **Note:** > This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). ## Overview In this tutorial, you'll learn how to use Velo to implement cross-selling in your store. Cross-selling means suggesting a product to your customers when they get to the checkout page. Cross-selling can increase cart value, overall revenue, and conversion rates while improving the shopping experience. You can see a similar implementation of the functionality described in this tutorial on [this site](https://www.wix.com/corvid-examples/upsell-products/cart-page?appSectionParams=%7B%22origin%22%3A%22cart-popup%22%7D), but with a different code solution. In both this tutorial and the example site, the site promotes Jasmine soap only if customers have one of the other products in their cart, but not Jasmine.
### How to Build This Promotion In the first part of this tutorial, we are going to set up the Cart Page using standard Editor elements. Then we'll use Velo and code to customize the Cart. ## Part I - Setting up the Cart Page ### Step 1: Start Working with Velo * Enable Velo Dev Mode in the Wix Editor to let you work with code. Click **Dev Mode** in your site's top bar and turn on **Enable Developer Mode** in the dropdown. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/703598e8-0a86-4968-a251-e6562cdf4684/2022/12/19/4070d4d3-bc7f-4740-b56e-14999e262e15/3c57ef51-8cdd-4f74-ae81-3568e830e83f.png) You will see the **Code sidebar** on the left-hand side of your screen, and the **Properties & Events** **panel** in the bottom-right corner of the Editor. * To get to the Cart page in your store, click the **Page Code** button in the **Code sidebar** on the left-hand side of the Editor, then **Cart page**. ### Step 2: Adding Elements Now we're going to add an element to hold the details of the product you want to promote: name, price, image, and description. 1. Click **Add** on the left side of the **Editor,** then **Box**, and then select **Container Boxes**. 2. Add a text element to the Container for the product name, another for the price, and one more for the description. 3. Add an image (choose one from your gallery). 4. Add a button and change its text to **Add to cart**. > **Note:** > Make sure you see the Attach to Box message before dropping all your elements into the container box. 5. Customize the elements in the Editor to match your store's design. ## Part II - Customizing the Cart Page with Code Now that you've set up your Cart page you can add the code to cross-sell your product. ### Step 3: Element IDs in the Properties Panel You need to start working with your site's elements in code. One of the first things to do is to give your elements meaningful IDs. That will make it easier to work with them in code. You change an element's ID in the Properties & Events Panel. When you select an element, its properties are displayed in the Properties & Events Panel.
> > If you can't see the Properties & Events Panel, click Tools in your site's top bar and check the box next to Properties & Events Panel in the dropdown. Select each element that you added to the container and change their IDs as described below: * Container box: crossSellBox * First text element: productName * Second text element: productPrice * Third text element: productDescription * The image: productImage * The button: addToCart
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/08/20/15f5af53-b39e-47ba-b403-7e105ce25222/65fe81c0-403e-4755-9060-3cfc9b50ed7f.png)
### Step 4: Product SKU 1. Navigate to the product section of your store dashboard. 2. Choose the product you plan to promote. 3. Note the product's SKU (stock keeping unit). This number is very useful for keeping track of inventory. You will need to assign SKUs to your products. Learn more about [Adding and Customizing Product Options](https://support.wix.com/en/article/wix-stores-adding-and-customizing-product-options#setting-inventory-preferences).
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/ef9147e1-f2d9-4773-9a14-7bc889c76065/2020/08/24/c5e04567-99b6-46c4-9b69-92e0ded7c12d/5e8e406f-a70a-42c7-b176-6bb884e0942d.png)
### Step 5: Cart Page Code Editor 1. Go back to the **Cart page** in the Editor. 2. In the Editor, paste the following code into the code editor for the Cart Page. >**Note:** > If you already have code in the Cart Page, take care to avoid clashes between the code you've written and the code provided below. Pay particular attention to the [onReady function](https://support.wix.com/en/article/corvid-best-practices-for-building-a-corvid-website#code-only-one-onready-per-page). ```javascript import wixData from 'wix-data'; import { getCurrentCart } from 'backend/cart.web'; const CROSS_SELL_PRODUCT_SKU = "0002"; $w.onReady(function () { checkIfProductMissingFromCart().then(isProductMissingFromCart => { const shouldShowCrossSellProduct = isProductMissingFromCart; if (shouldShowCrossSellProduct) { getCrossSellProduct().then(crossSellProduct => { renderCrossSellProduct(crossSellProduct); }) } else { $w("#crossSellBox").collapse(); } }) }) async function checkIfProductMissingFromCart() { const { lineItems } = await getCurrentCart(); const isProductMissingFromCart = lineItems.every(lineItem => lineItem.sku !== CROSS_SELL_PRODUCT_SKU); return isProductMissingFromCart; } async function getCrossSellProduct() { const { items: productsMatchingSKU } = await wixData.query('Stores/Products').eq('sku', CROSS_SELL_PRODUCT_SKU).find(); const crossSellProduct = productsMatchingSKU[0]; return crossSellProduct; } function renderCrossSellProduct(product) { $w("#productName").text = product.name; $w("#productPrice").text = product.formattedPrice; $w("#productImage").src = product.mainMedia; const productDescriptionWithoutTags = product.description.replace(/(<([^>]+)>)/ig, ""); $w("#productDescription").text = productDescriptionWithoutTags; $w("#addToCart").onClick(async () => { await $w("#shoppingCartIcon1").addToCart(product._id, 1); }) } ``` #### Understanding the Code **Line 1**: Import the module you need to work with Wix Data. **Line 2**: Import your backend code. **Line 4**: This is where you will insert your product's SKU. If you'd like to promote a different product, replace **only** the number inside the double quotes with a different SKU. For example, if your product's SKU is 213, replace line 4 with: `const CROSS_SELL_PRODUCT_SKU = "213";` **Lines 6-13**: When the page loads, call the other functions in the code to: check for your promoted product in the cart (lines 20-25); find the product in your products database (lines 27-32); display the product information in the cross-sell box (lines 34-44). **Lines 14-15**: If the buyer visiting your store has already added your promoted product to their cart, don't show the cross-sell box. **Lines 20-25**: Check the currently displayed cart for the product you've decided to promote. **Lines 27-32**: Find the promoted product in your products database and get its information. **Lines 34-44**: Display the product name, price, image, and description in the elements inside the cross-sell box. ### Step 6: Backend Code 1. Click the **Code Files** button in the **Code sidebar**, then hover over **Backend**, click the plus icon, and select **Add Web Module.** 2. Name this module cart.web.js. 3. Delete the provided sample code and paste the following code into the Web Module. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import wixStores from 'wix-stores-backend'; export const getCurrentCart = webMethod(Permissions.Anyone, () => { return wixStores.getCurrentCart(); }); ``` #### Understanding the Code **Line 1**: Import the `Permissions` enum and `webMethod` from `wix-web-module`. **Line 2**: Import the module you need to work with Wix Stores. **Line 4-5**: Export the `getCurrentCart()` function for use on the front-end (line 2 of the cart page code). Learn more [here](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md). ### Step 7: Save and Publish your Site. * In the top right corner of the Editor, save and preview to check your progress. * To see the live version of the site, click **Publish.**
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Request a Quote ## Article: Tutorial: Request a Quote ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-request-a-quote.md ## Article Content: # Tutorial: Adding a Request A Quote Option to a Wix Store Product Page > This feature is currently only available for [Wix Studio](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md) sites. This article demonstrates how you can add an option for site visitors to request a quote for a specific product on a Wix Stores Product Page. A site visitor may want to add this option, for example, to request a discount when purchasing a large quantity of a specific product, or to request a product with custom dimensions and specifications. In this example, we show how you can replace either the **Add To Cart** or the **Buy Now** button with a **Request a quote** button on a Wix Store Product Page. When a site visitor clicks **Request a quote**, a modal with a submission form opens to submit the quote request to the site owner. The site owner can then see the submission in the **Form submissions** tab in the site's dashboard. > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. To do this, we: 1. Add a [popup](https://support.wix.com/en/article/studio-editor-using-popups) to create the modal. 2. Create a submission form and add it to the popup. 3. Write the code for our Product Page to change the specific product's **Add To Cart** button's label to **Request a quote**, and to connect the button to the modal. 4. Implement the [eCommerce Validations SPI custom extension](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations). You can also use this example for other use cases such as adding a **Contact us to Purchase** option. >**Note:** This tutorial provides an example of how you can override the **Add To Cart** or **Buy Now** button's functionality using the [`onAddToCart()`](https://www.wix.com/velo/reference/$w/productpage/onaddtocart) or [`onBuyNow()`](https://www.wix.com/velo/reference/$w/productpage/onbuynow) function. ## Before You Begin + This article assumes you have a [Wix Stores Product Page](https://support.wix.com/en/article/wix-stores-customizing-the-new-product-page) on your site. + For any product in a Wix Stores Product Page, you can provide site visitors with the option to either purchase a product (with predefined specifications, quantities, and price), or to request a quote. However, if you only want to provide site visitors with the option to request a quote for a specific product, and don't want them to be able to purchase it, you need to implement the [eCommerce Validations SPI custom extension](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations). This custom extension can be used to validate the site visitor's cart and checkout to make sure that the specific product can't be purchased. We show this implementation in the last step of this tutorial. To learn more about custom extensions, see [Custom App Extensions Using SPIs](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md). ## Introduction We created the following custom bookshelf on a Wix Store Product Page for this tutorial. When a site visitor navigates to this Product Page, we want them to see a **Request a quote** button instead of the **Add To Cart** button. When a site visitor selects their [product options](https://support.wix.com/en/article/wix-stores-adding-and-customizing-product-options) for the bookshelf and clicks the **Request a quote** button, we want a modal with a submission form to open, and we want to stop the site visitor from proceeding with the Wix Checkout process. Once the site visitor fills out and submits the form, we want the site owner to receive the form information along with the site visitor's selected product option choices. ![product page add to cart](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2de05a3808854f230a3c7dda42c1cc70.png) ## Step 1: Create the modal First we create the modal by adding a [popup](https://support.wix.com/en/article/studio-editor-using-popups) to our site from the **Layout Tools** section of the **Add** panel. We name our popup 'Request a quote'. ## Step 2: Create a submission form Next, we add a submission form from the **Contact & Forms** section of the **Add** panel, and place it in the popup. Our form has the following fields and looks like this: ![request a quote popup](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e0f09f843980e40eb827068a4177cd30.png) ## Step 3: Write the code Last, we write the code for our Product page. We break the code into steps to understand how it works: 1. First we import our frontend modules and declare our variables. ```js import wixWindowFrontend from 'wix-window-frontend'; import wixLocationFrontend from 'wix-location-frontend'; let product; const bookshelfId = 'cd59cd36-b6d2-2cf3-9d48-81793a7bdbbd'; ``` **Understanding the Code** **Lines 1-2:** We import the `wixWindowFrontend` and `wixLocationFrontend` modules. **Lines 4-5:** We declare our variables to be used later in our code, including the bookshelf ID. To get the bookshelf ID, do the following: 1. In the editor, open the CMS panel on the left and click **Wix App collections**. 2. Scroll down to the **Stores** section and click **Products** to open the Products collection. 3. Find the bookshelf item in your collection, and copy the value in the `ID (_id)` field. In our example, the value should be `cd59cd36-b6d2-2cf3-9d48-81793a7bdbbd`. This is your bookshelf ID. To learn more, see [Working with Wix App Collections and Code](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md). 2. When the Product Page loads or when a site visitor selects a different product in the Product Page, we get the product's information. If the ID of the product on the Product Page matches the bookshelf's product ID, then we change the **Add To Cart** button's label to **Request a quote**, and add an event handler that runs when this button is clicked. ```js $w.onReady(function () { initProductPage(); }); wixLocationFrontend.onChange((location) => { let newPath = location.path; if (newPath?.[0] === 'product-page') { initProductPage(); } }); async function initProductPage() { product = await $w('#productPage1').getProduct(); if (product._id === bookshelfId) { $w('#productPage1').setAddToCartLabel('Request a quote'); $w('#productPage1').onAddToCart(onAddToCartHandler); } } ``` **Understanding the Code** **Lines 1-3:** When the Product Page loads, we call the `initProductPage()` function. **Lines 5-10:** When a site visitor selects a different product on the Product Page, we call the `initProductPage()` function. **Lines 12-13:** In the `initProductPage()` function, we get the information of the product on the Product Page. **Lines 14-18:** If the ID of the product on the Product Page matches the bookshelf's product ID, then we use the `setAddToCartLabel()` function to change the **Add To Cart** button's label to **Request a quote**, and call the `onAddToCart()` event handler when this button is clicked. >**Note:** You can change the **Buy Now** button's label to **Request a quote** instead by replacing the [`setAddToCartLabel()`](https://www.wix.com/velo/reference/$w/productpage/setaddtocartlabel) function with the [`setBuyNowLabel()`](https://www.wix.com/velo/reference/$w/productpage/setbuynowlabel) function, and the [`onAddToCart()`](https://www.wix.com/velo/reference/$w/productpage/onaddtocart) function with the [`onBuyNow()`](https://www.wix.com/velo/reference/$w/productpage/onbuynow) function. After implementing the code, the Product Page should look like this: ![product page request a quote](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3a06f7acc786919c1bee0dd20a679b4c.png) 3. When a site visitor clicks the **Request a quote** button, get the site visitor's selected product option choices (color, number of shelves, and measurements), open the modal with the submission form, and stop the site visitor from proceeding with the Wix Checkout process. ``` js async function onAddToCartHandler(resume, cancel) { const data = await getSelectedProductData(); wixWindowFrontend.openLightbox('Request a quote', data) .then((bookshelfOptionData) => { console.log(bookshelfOptionData) cancel(); }); } async function getSelectedProductData() { const bookshelfQuantity = await $w('#productPage1').getQuantity(); const bookshelfChoices = await $w('#productPage1').getSelectedChoices(); const bookshelfCustomText = await $w('#productPage1').getCustomText(); return { product: product, quantity: bookshelfQuantity, choices: bookshelfChoices, customTextFields: bookshelfCustomText, } } ``` **Understanding the Code** **Line 1:** We write an `onAddToCartHandler()` function to handle the event when a site visitor clicks the **Request a quote** button, and pass in the `resume` and `cancel` functions. **Lines 2-8:** In the `onAddToCartHandler()` function, we call the `getSelectedProductData()` function to get the bookshelf option choices that the site visitor selected. Next we open our 'Request a quote' popup, passing in the bookshelf option data. We then console log the data and call the `cancel()` function to stop the site visitor from proceeding with the Wix Checkout process. **Lines 10-20:** In the `getSelectedProductData()` function, we use the `getQuantity()` function to get the bookshelf's quantity, the `getSelectedChoices()` function to get the selected color and number of shelves, and the `getCustomText()` function to get the measurements (Width/Height/Depth). We then return this data. ### Full Code Example ```js import wixWindowFrontend from 'wix-window-frontend'; import wixLocationFrontend from 'wix-location-frontend'; let product; const bookshelfId = 'cd59cd36-b6d2-2cf3-9d48-81793a7bdbbd'; $w.onReady(function () { initProductPage(); }); wixLocationFrontend.onChange((location) => { let newPath = location.path; if (newPath?.[0] === 'product-page') { initProductPage(); } }); async function initProductPage() { product = await $w('#productPage1').getProduct(); if (product._id === bookshelfId) { $w('#productPage1').setAddToCartLabel('Request a Quote'); $w('#productPage1').onAddToCart(onAddToCartHandler); } } async function onAddToCartHandler(resume, cancel) { const data = await getProductInfo(); wixWindowFrontend.openLightbox('Request a quote', data) .then((results) => { cancel(); }); } async function getProductInfo() { const bookshelfQuantity = await $w('#productPage1').getQuantity(); const bookshelfChoices = await $w('#productPage1').getSelectedChoices(); const bookshelfCustomText = await $w('#productPage1').getCustomText(); return { product: product, quantity: bookshelfQuantity, choices: bookshelfChoices, customTextFields: bookshelfCustomText, } } ``` ## Step 4: Implement the Validation SPI custom extension The last step we need to do is validate the cart and checkout using the [eCommerce Validations SPI custom extension](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations) to make sure that a site visitor can only request a quote for the bookshelf and can't purchase it. To do this: 1. In the Studio Editor, click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png), then **Packages & Apps**. 2. Hover over Custom Extensions and click **eComm Validation SPI** to add the validations integration. Then follow the prompts to add the extension and accept any terms and conditions that display. 3. Enter a name for your integration and click **Add & Edit Code**. We named ours **validation-spi**. The name can't contain spaces or special characters. Open the Wix IDE, and go to **/src/backend/__spi__**. You should see the spi folder there. It should look something like **ecom-validations/validation-spi**. If the Custom Extension doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 4. In this spi folder, you should see 2 files, **validation-spi.js** and **validation-spi-config.js**. We write the following code in the **validation-spi-config.js** file to validate the cart as well as the checkout. ```js import * as ecomValidations from 'interfaces-ecommerce-v1-validations-provider'; export function getConfig() { return {validateInCart: true}; } ``` **Understanding the Code** **Line 1:** We import all modules from the ecom validations provider. **Lines 3-5:** We want to also validate a site visitor's cart, so we set the `validateInCart` parameter to `true` in the exported config function. 6. We write the following code in the **validation-spi.js** file to validate whether the bookshelf is in the site visitor's cart or checkout. If so, we return the validation violation and give a violation description. ```js import * as ecomValidations from 'interfaces-ecommerce-v1-validations-provider'; const bookshelfId = 'cd59cd36-b6d2-2cf3-9d48-81793a7bdbbd'; export const getValidationViolations = async (options, context) => { let violations = []; const severity = ecomValidations.Severity.ERROR; const description = "You can't purchase this product. You can only request a quote." const requestAQuoteLineItem = options.validationInfo.lineItems.find(lineItem => isBookshelf(lineItem.catalogReference?.catalogItemId)); if (requestAQuoteLineItem != undefined) { const quantity = requestAQuoteLineItem.quantity; if (quantity > 0 && quantity < 200) { violations.push({ severity, target: { lineItem: { name: ecomValidations.NameInLineItem.LINE_ITEM_DEFAULT, _id: requestAQuoteLineItem._id } }, description: description }); } } return { violations }; }; const isBookshelf = (catalogItemId) => { return catalogItemId != undefined && (catalogItemId == bookshelfId); } ``` **Understanding the Code** **Line 1:** We import all modules from the ecom validations provider. **Line 3:** We declare the bookshelf product ID as a variable. **Line 5:** We declare a validation logic to run on each item in the cart and checkout. **Line 6:** We declare an empty array of validation violations. **Lines 7-8:** We define the validation to prevent the site visitor from proceeding with the Wix Checkout process, and define the validation message. **Line 9:** This line searches for a specific line item in the `options.validationInfo.lineItems` array. It uses the find method and a callback function that checks if the line item’s `catalogItemId` matches the `bookshelfId` using the `isBookshelf()` helper function. **Line 10-25:** If we find a match, we add a validation rule to prevent the site visitor from purchasing this item. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Payment Settings Service Plugin ## Article: Tutorial: Payment Settings Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-payment-settings-service-plugin.md ## Article Content: # Tutorial: Payment Settings Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site behaves and displays. For example, when you set up a Wix eCommerce site there are a number of settings that apply by default during the payment process. The payment settings service plugin allows you to implement your own payment settings using code. These settings will then apply during the payment process. With the Payment Settings service plugin you can apply custom payment settings, such as requiring an additional layer of security, to an order during the payment process. Learn more about on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md). ## Introduction This tutorial explains how to set up and implement a payment settings service plugin on your site using Velo. We created an example to demonstrate how you can use plugins to apply [3-Domain Security](https://support.wix.com/en/article/about-3d-secure-3ds-payments-with-third-party-payment-providers)(3DS) when the price of the order is greater than or equal to $50. The process has 3 steps: 1. Create a new payment settings service plugin on your site. 2. Implement your service plugin with custom code. 3. Deploy the plugin. ### Before you begin To collect payments, a Wix site must [connect a payment provider](https://support.wix.com/en/article/connecting-a-payment-provider). Once a payment provider is connected you may use the Payment Settings service plugin to enforce additional payment settings to your site's transactions. For example, a site may require additional [3d secure payments](https://support.wix.com/en/article/about-3d-secure-3ds-payments-with-third-party-payment-providers) (3DS) for certain orders. Note that each payment provider may have specific payment settings they accept with their Wix integration. For example, [Tranzila](https://support.wix.com/en/article/connecting-tranzila-as-a-payment-provider) is a payment provider that supports 3DS payments, but not all payment providers offer this feature. We recommend [contacting payment providers directly](https://support.wix.com/en/article/receiving-payouts-from-third-party-payment-providers) to confirm which payment settings they have implemented as part of their Wix integration. Currently, the only payment setting available to customize is whether to apply 3DS to an order. If 3DS is required, the customer will have to pass an additional layer of security before completing their payment. For example, they will have to pass a window like this before continuing: ## Step 1: Create a new payment settings service plugin The first step in setting up your new service plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or the **/src/backend/spi** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. If necessary, [add the Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) to your site. 2. Enable coding: + **Wix Studio:** If necessary, in the Code sidebar, click `{}` and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** `{}` tab in the Code sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Public & Backend**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/badbd3b0-193b-4ada-b213-dc3233752d5c/7dca33d4-2b2d-4d12-9082-bafdf7ca5440.png), then click **Payment Settings**. 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your service plugin and click **Add & Edit Code**. The name can't contain spaces or special characters. In our example, we named ours **require-3ds-50**. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/__spi__/**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the service plugin The procedure in the previous section creates a folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio), called **ecom-payment-settings**. Inside this is another folder with the name of the plugin we set up. This folder contains 2 default service plugin files: * `-config.js`: The code in this file defines a function that returns an object containing values used to configure your plugin. * `.js`: The code in this file defines a function specific to the service plugin, such as `getPaymentSettings()`. The function is called by Wix to retrieve the data provided by your plugin. We implement the custom code for your service plugin in these files. In our example, the folder looks like this (Wix Studio): ![Payment Settings Plugin](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f86b7bfc7660639acf6bb00f7d89dfbe.png "Payment Settings Plugin") ## require-3ds-50-config.js This file is where we write the code for setting up the plugin's configuration. The code in this file defines a function named `getConfig()` that returns an object containing a default configuration for our payment settings. In our example, we set `fallbackValueForRequired3dSecure` to `true` so if the call fails it will, by default, return `paymentSettings.requires3dSecure` set to `true` which tells the payment provider to require 3DS for the order. ```js import * as ecomPaymentSettings from 'interfaces-ecommerce-v1-payment-settings-provider'; export function getConfig() { return { fallbackValueForRequired3dSecure: true}; } ``` ## require-3ds-50.js This file is where we write the code to require 3DS to an order during payment if the price of the order is $50 or greater. The code in this file defines a function named `getPaymentSettings()`. This function is called by Wix eCommerce during the payment process to retrieve the specific payment settings to apply to an order. The function accepts the following parameter: `options`: An object containing information about the order. For details on what's included in the order, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-payment-settings/getpaymentsettings). The `getPaymentSettings()` function returns a `paymentSettings` object containing the payment settings to apply to the order. Currently, the only setting included is `requires3dSecure`. ## The code: ```js import * as ecomPaymentSettings from 'interfaces-ecommerce-v1-payment-settings-provider'; export const getPaymentSettings = async (options, context) => { return { paymentSettings: { requires3dSecure: parseFloat(options.order.priceSummary.subtotal.amount) >= 50 } }; }; ``` **Line 1-3:** First we import the eCom payment settings module. Then we export the `getPaymentSettings()` function where we set the custom logic to apply to the order. **Line 6:** We extract the subtotal price from the order (`options.order.priceSummary.subtotal.amount`) and apply `parseFloat` to convert the string into a floating-point number. We then check if that number is equal or greater than `50`. If so, `requires3dSecure` is set to `true`, meaning that 3DS is required for this order. If this logic fails, for example if somehow the `amount` isn't a number, then `requires3dSecure` will be set to the value set for `fallbackValueForRequires3DSecure` in the config.js file. ## Optional: Add files to the service plugin If you don't want to keep all of your code in the main service plugin files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Editor:** + Hover over the service plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . + Select **New.js**. + **Wix Studio:** Create a new file in the plugin's folder in the Wix IDE Explorer. To import from these files to the main files, use the following syntax: ```js import { functionName } from '.myFileName.js'; ``` ## Optional: Test the service plugin (Wix Editor) You can test your plugin before publishing your site using [functional testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) in the Wix Editor like you would with any backend Velo code. Make sure your `getPaymentSettings()` function's return values are properly formatted. Note that functional testing is not currently supported in Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 3: Deploy the plugin Once your code files are ready, publish your site. Navigate to your site and place an order. If the order is $50 or greater than 3DS should be required to complete the payment process. >**Note:** > > There may be a delay between publishing the site and the new payment settings applying on the live site. ## Example We built a [sample site](https://editor.wix.com/html/editor/web/renderer/new?siteId=16f79a4d-9bc2-4888-a12d-1c0f1ce1c31c&metaSiteId=3bf2529d-f59f-485d-a5f1-136755294d13&autoDevMode=true) where you can see this code in action. > **Note:** Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. ## Remove a plugin You can remove an plugin from your site. **Wix Editor:** 1. In the **Public & Backend** section of the Code sidebar, under **Service Plugins**, hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/c4ff84cb-4d96-4e1a-966a-9c6b66dca0b2/062297c2-c368-4aee-b705-f0a55abc5bf6.png) . 2. Select **Delete**. 3. Click **Delete**. **Wix Studio:** Right click on the plugin's folder and select **Delete Permanently**. ## Legal notices If you connect to a 3rd-party provider using service plugins, you agree to the [Wix.com Terms of Use](https://www.wix.com/about/terms-of-use). Wix is not responsible for your use of such a 3rd-party provider, and any liability resulting from such use will be your responsibility. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: eCommerce Additional Fees Service Plugin ## Article: eCommerce Additional Fees Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-additional-fees-service-plugin.md ## Article Content: # Velo Tutorial: eCommerce Additional Fees Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site behaves and displays. For example, when you set up a Wix eCommerce site, there are limitations to how you can charge buyers of your products. What if you want to charge buyers an extra fee for gift-wrapping an item? What if you want to charge a carbon emission fee? The answer is service plugins. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md). With the Additional Fees service plugin, you can define the additional fees that fit your site's needs. You can also customize code to calculate different types of additional fees.  For example, you can add some of the following additional fees to your checkout flow:  * Gift wrapping fees * Shipping insurance fees * Item warranty fees * Credit card service fees * Shipping and handling fees * Fragile packaging fees * Carbon offset fees These additional fees are displayed in your store's **Cart** and **Checkout** pages.  This article explains how to set up an Additional Fees plugin on your site using Velo. The process has 3 steps: 1. Create a new Additional Fees service plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. >**Note** > In order to add the Additional Fees service plugin to your site, you need to install the [Wix Stores App](https://support.wix.com/en/article/wix-stores-adding-wix-stores) or the [Wix Bookings App](https://support.wix.com/en/article/wix-bookings-adding-wix-bookings-from-the-wix-owner-app). ## Step 1: Create a new Additional Fees service plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or the **/src/backend/__spi__** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. Add the [Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) or the [Wix Bookings app](https://support.wix.com/en/article/wix-bookings-adding-wix-bookings-from-the-wix-owner-app) to your site. 2. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Code sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Public & Backend**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png) , then click **Additional Fees**. 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/__spi__/ecom-additional-fees**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Code sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio). The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, `.js` and `-config.js`. Implement the custom code for your plugin in these files. Below are some guidelines for writing your code.  ### `.js` The code in this file defines a function called `calculateAdditionalFees()`. This function is called by Wix eCommerce when an action occurs in the cart or on the checkout page, and then receives the additional fees provided by your plugin. The function contains the following parameter: * **options**: An object containing data and shipping information about the items in your cart. Your plugin can use this data to calculate any additional fees. For more details, see the [Additional Fees Service Plugin Reference.](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-additional-fees)  Example **options** object: ```json [ { "lineItems":[ { "physicalProperties":{ "sku":"0001", "shippable":true }, "quantity":1, "price":"4.00", "_id":"00000000-0000-0000-0000-000000000004", "rootCatalogItemId":"cb688748-3f3d-e5f4-accb-5bf894a4ecd0", "productName":"Luxury glass", "catalogReference":{ "catalogItemId":"cb688748-3f3d-e5f4-accb-5bf894a4ecd0", "appId":"215238eb-22a5-4c36-9e7b-e7c08025e04e", "options":{ "options":{ }, "variantId":"00000000-0000-0000-0000-000000000000" } } } ], "shippingInfo":{ "selectedCarrierServiceOption":{ "code":"fc538775-9417-87a9-6808-c71996de4723", "logistics":{ "deliveryTime":"3-5 days", "instructions":"Deliver during opening hours.", "pickupDetails":{} }, "carrierId":"c8a08776-c095-4dec-8553-8f9698d86adc", "title":"standard shipping", "requestedShippingOption":"true", }, "carrierServiceOptions":[ { "carrierId":"c8a08776-c095-4dec-8553-8f9698d86adc", "shippingOptions":[ { "code":"fc538775-9417-87a9-6808-c71996de4723", "title":"standard shipping", "logistics":{ "deliveryTime":"3-5 days", "instructions":"Deliver during opening hours.", "pickupDetails":{} } } ] } ] } } ] ``` The `calculateAdditionalFees()` function returns an object containing an array called `additionalFees` and a property called `currency`, defining the additional fees and currency that appears in the **Cart** and on the **Checkout** pages. For more details, see the [Additional Fees Service Plugin Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-additional-fees). Example return value: ```json { additionalFees: [ { code: "wrapping-fee", name: "Wrapping Fee", price: "5", taxDetails: { taxable: false } } ], currency: "EUR" } ``` >**Note** > The **currency** you return must be the same as the site's currency. ### `-config.js` The code in this file defines a function named `getConfig()` that returns an empty object. Example: ```json export function getConfig() { return {}; } ``` ### Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png). 2. Select the **New.js** file. + **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ## Test the plugin You can test your plugin before publishing your site in the Wix Editor using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) like you would any backend Velo code. Make sure your `calculateAdditionalFees()` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console log statements to your code. The results appear in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). ## Step 3: Deploy the plugin Once your code files are ready, publish your site. Navigate to your site, and add an item to your Cart. Any additional fees you've customized are displayed in the cart and on the checkout pages. if they pertain to your cart items.  1. Once your code files are ready, click **Save**.  2. **Publish** your site.  3. After your service plugins are published, open your site. 4. Navigate to your **Cart** or **Checkout** page, and make a change. For example, add an item to your cart, or select a shipping location. 5. See your custom additional fees appear in your site's **Cart** and in your site's **Checkout** pages.  ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/a551efd3-24d0-48fe-8aec-00f810ffc5fc/2022/09/01/99f540fc-3cc3-4b45-b952-c921ce89f03a/f6f5cbe0-8ef0-4ec1-ae09-333b98e7d6c7.png) ## Example We created an example to demonstrate how you can use the service plugins feature to extend the additional fees on your site.  > **Note:** This example and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#finding-your-way). ### Wrapping Fee In this example, a wrapping fee has been integrated into a store's site. The code in the `additional-fees.js` file counts all the items in the cart that contain the words `glass`, `bowls`, `vase`, or `plates` in their names. These items are considered fragile. The wrapping fee for each fragile item in a customer's cart is then calculated. If a cart contains at least 5 fragile items, the wrapping fee is not applied. We built a sample site where you can see this code in action. To set up a working copy of the sample site, do the following: 1. Open a copy of the [sample site](https://editor.wix.com/html/editor/web/renderer/new?siteId=fc57098b-9c22-415e-950a-ce5edce5e1e2&metaSiteId=97fcc7f0-2cb2-4b33-86e3-45fbe430fc5e). 2. [Publish the site](https://support.wix.com/en/article/wix-editor-publishing-your-site#publishing-your-site) to save the copy to your Wix account. 3. In your new site's dashboard, [go to Settings](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Settings&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/settings). 4. Click **Language & Region**. 5. Scroll down to **Site Currency**. 6. Click the **Currency** drop-down menu and select **US Dollar - USD ($)**. 7. Click **Save**. >**Note** > The sample site is not a premium Wix site. This means you can only see the extra fees displayed in the site's cart and not on the checkout page. ## Remove an plugin You can remove an plugin from your site. **Wix Editor:** 1. In the **Public & Backend** section of the Code sidebar, under **Service Plugins**, hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/c4ff84cb-4d96-4e1a-966a-9c6b66dca0b2/062297c2-c368-4aee-b705-f0a55abc5bf6.png) . 2. Select **Remove**. 3. Click **Remove**. **Wix Studio:** Right click on the plugin's folder and select **Delete Permanently**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: eCommerce Catalog Service Plugin ## Article: eCommerce Catalog Custom Extension (Beta) ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-catalog-service-plugin.md ## Article Content: # Velo Tutorial: eCommerce Catalog Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site behaves and displays. For example, when you set up a Wix eCommerce site, there are a limited number of catalogs to choose from and some limitations on how you can define the products and services you wish to sell. To describe your product in a way that is not currently natively supported, or create a simplified catalog of services, you can use a service plugin. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md). This guide explains how to set up a catalog service plugin on your site using Velo. The process has 3 steps: 1. Create a new catalog service plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. ## Step 1: Create a new catalog service plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Velo sidebar (Wix Editor), or the **/src/backend/__spi__** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. [Add the Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) to your site. 2. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Velo sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Packages & Apps**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png) , then click **Catalog**. 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/__spi__/ecom-catalog**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. 9. **Open** [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md) window and keep it open. There should be a log with the text: Your site was published with an update to the ecom catalog plugin, 'my-plugin-name' (**appId: ``**). 10. Save/copy the **appId** you received in the log. You will need this when returning catalog items from your plugin. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Velo sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio). The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, `.js` and `-config.js`. Implement the custom code for your plugin in these files. Below are some guidelines for writing your code.  ### ``.js The code in this file defines a function named `getCatalogItems()`. This function is called by Wix eCommerce to retrieve the catalog items provided by your service plugin. The function accepts the following parameter: `options`: An object containing catalog item references (within your own catalog), quantities, and weight unit. This data is to be used by your plugin to retrieve your catalog items. These details are requested by Wix eCommerce when, for example, an item is added to your site's cart. For more details, see the [service plugin Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-catalog/introduction). >**Note** > If you also have Wix Stores and Wix Bookings catalogs on your site, when `getCatalogItems()` is called, the `options` parameter only contains references to your custom catalog items. The Wix Stores and Wix Bookings catalog items are automatically returned. Example **options** object: ```json { "catalogReferences": [ { "catalogReference": { "catalogItemId": "2", "appId": "7ff4b539-e3bf-4d8e-84d4-2ed180fce336", "options": { "week": "3" } }, "quantity": 1 } ], "weightUnit": "LB" } ``` >**Note** > The value of `weightUnit` is based on the regional format set on your site. To set a regional format open your site’s [dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Go%20to%20Dashboard&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7B%7BmetaSiteId%7D%7D/home/) and go to **Settings > Language & Region**. The `getCatalogItems()` function must return an object with a key called `catalogItems`. The value of this key is an array of `catalogReference` and `data` objects. These objects define the catalog items' references in your catalog, as well as each item's data. For the value of `catalogReference.appId`, use the `appId` you received in the log from [Step 1.9](#step-1-create-a-new-catalog-custom-extension) described above. In the `data` object, `productName`, `itemType`, and `price` are required fields. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-catalog/getcatalogitems). Example return value: ```json { "catalogItems": [ { "catalogReference": { "appId": "", "catalogItemId": "2", "options": { "week": "3" } }, "data": { "productName": { "original": "EuroCamper" }, "itemType": { "preset": "PHYSICAL" }, "price": "540", "media": "wix:image://v1/11062b_7fba2fc327a04e1493f1a28213e8cae8~mv2.jpeg/camping-site#originWidth=6924&originHeight=3130", "descriptionLines": [ { "name": { "original": "Beds" }, "plainText": { "original": "Type: Double, Quantity: 1\nType: King, Quantity: 1" } }, { "name": { "original": "Size" }, "plainText": { "original": "7x3x3" } }, { "name": { "original": "Description" }, "plainText": { "original": "Professional Travelers RV for road trips, Enjoy a comfortable mobile home for the best family experience !" } }, { "name": { "original": "Additional Info" }, "plainText": { "original": "Deposit fee is 40$ per item\nReturning the item: return shipping is included with your order. All\nyou have to do is re-pack the hardware and it will be picked up by\nthe shipping company at the rental end date. If you cannot be\nreached, late fees will apply.\nCancelation: Orders will receive a full refund when canceled 10\nbusiness days before the rental start date." } }, { "name": { "original": "Week" }, "plainText": { "original": "3" } } ], "physicalProperties": { "shippable": false }, "quantityAvailable": 2 } } ] } ``` ### `-config.js` The code in this file defines a function named `getConfig()` that returns an object containing configurations for the catalog. Currently no configurations are available. Example return object: ```json export function getConfig() { return {} } ``` ### Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . 2. Select the **New.js** file. + **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ### Test the plugin To test your plugin, create a custom product page based on your catalog's items. Write code that calls the [`addToCurrentCart()`](https://dev.wix.com/docs/velo/api-reference/wix-ecom-backend/current-cart/add-to-current-cart.md) function (see [Limitations](#Limitations) section below). Make sure to pass the items' catalog references in the `options.lineItems.catalogReference` parameter. This should be similar to the `catalogReference` object [described above](#my-integration-namejs). Make sure to use your catalog's `appId` - see [Step 1.9](#step-1-create-a-new-custom-catalog-extension). You can test your plugin before publishing your site in the Wix Editor using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) like you would any backend Velo code. Make sure your `getCatalogItems()` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). ## Step 3: Deploy the plugin Once your code files are ready, publish your site. Any catalog items you've integrated into dynamic pages are displayed on the relevant pages. When you add an item to the cart, the item details are displayed in the cart (and then later in the checkout and order flows).  ## Examples We created some examples to demonstrate how you can use service plugins to create your own custom catalog. > **Note:** These examples and their steps are based on Wix Editor sites. If you'd prefer to use Wix Studio, learn more about [working with the code panel in Wix Studio](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md). ### Poems Catalog In this example, a simple catalog of poems has been integrated into the site. The catalog plugin uses a [collection in the Content Management System (CMS)](https://support.wix.com/en/article/content-manager-creating-a-collection) as the catalog source. The `poems-catalog.js` plugin file includes code that fetches catalog item info from the poems collection using the [wix-data API](https://www.wix.com/velo/reference/wix-data), as well as a `toCatalogItems()` function that constructs the catalog item to be returned by the service plugin. We built a [sample site](https://editor.wix.com/html/editor/web/renderer/new?siteId=95d384b1-0f26-41fc-b8f2-08fb873d9b73&metaSiteId=0e57ff6f-b239-4b1c-aa20-891c6f8c6bf5) where you can see this code in action. >**Note** > Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. ### RV Rentals Catalog In this more complex example, several collections are used together to implement an RV rental catalog. Some collections hold the RV details themselves, while others hold availability and stock data. The site includes detailed dynamic product pages that display not only product info but also availability status depending on the selected rental dates. Have a look at the [sample site](https://editor.wix.com/html/editor/web/renderer/new?siteId=38fa7176-950b-46a2-a82c-50563d81e23b&metaSiteId=0ffe1191-0cf6-4927-98bd-c2ed3e338d51) where you can go through the code to see the implementation for yourself.  >**Note** > Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. ### Limitations After adding an item to the cart, the cart might not refresh. To fix this, implement the following code: * Put `import wixEcomFrontend from "wix-ecom-frontend";` at the top of the file. * Use the [`refreshCart();`](https://dev.wix.com/docs/velo/api-reference/wix-ecom-frontend/refresh-cart.md) function to force refresh the cart. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Custom Discount Trigger Service Plugin ## Article: Tutorial: Custom Discount Trigger Service Plugin ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-custom-discount-trigger-service-plugin.md ## Article Content: # Tutorial: Custom Discount Trigger Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site displays and behaves. For example, with Wix Stores or Wix Restaurants your site can set up discounts that apply automatically for specific products or based on specific conditions, such as a minimum quantity of items or a minimum order subtotal. This service plugin allows you to implement your own custom conditions that will trigger a discount for a customer. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md). With the Custom Discount Trigger service plugin you can define which conditions will trigger an automatic discount. Possible custom triggers could apply discounts on the following: * For members only. * A restaurant's menu based on the time of day. * Digital products. * Specific items based on size or color. * A bundle of related items in a cart. ## Introduction This tutorial explains how to set up and implement a custom discount trigger plugin on your site using Velo. We created an example to demonstrate how you can use service plugins to create custom triggers that apply automatic discounts for your customers. We use the example of a restaurant that wants to offer a 25% off discount on all menu items between the hours of 4-6 pm. The process has 4 steps: 1. Create a new custom discount trigger plugin on your site. 2. Implement your plugin with custom code. 3. Create a new discount and assign the custom trigger that triggers it. 4. Deploy the plugin. ## Step 1: Create a new custom discount trigger plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **/src/backend/__spi__** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code, or the **Service Plugins** section of the Velo sidebar (Wix Editor). 1. [Add the Wix Restaurants app](https://support.wix.com/en/article/wix-restaurants-an-overview) to your site. 2. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Velo sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Public & Backend**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Next to **Service Plugins**, click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png), then click **Custom Discount**. (In Wix Editor you need to hover over **Service Plugins**.) 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. In this example, we named it **custom-triggers**. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/__spi__/ecom-discounts-trigger**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the Wix IDE's Explorer (Wix Studio), or in the service plugins section of the Velo sidebar (Wix Editor). The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, **custom-triggers-config.js** and **custom-triggers.js**. We implement the custom code for our plugin in these files. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e-commerce-tutorial-custom-discount-trigger-service-plugin-md_velo-articles_wix-stores-with-velo_images_custom-trigger-plugin.png) ### custom-triggers-config.js This file is where we write the code for setting up the plugin's configuration. The code in this file defines a function named `getConfig()` that returns an object containing the configuration of your custom discount trigger plugin. Currently no configurations are available for this plugin, so set `getConfig()` to return an empty object. ```js import * as ecomDiscountsTrigger from 'interfaces-ecommerce-v1-custom-trigger'; export function getConfig() { return {}; } ``` ### custom-triggers.js This file is where we write the custom code that determines whether or not to trigger a discount. In our example, we want to trigger a 25% discount on all menu items if the customer is making the order between 4-6 pm. The code in this file creates a custom trigger with the `listTriggers()` function by returning an object with an `_id` and a `name`. Then, with the `getEligibleTriggers()` function we define the custom logic for when to apply the trigger we created. ```js import * as ecomDiscountsTrigger from 'interfaces-ecommerce-v1-custom-trigger'; // Define all custom triggers export const listTriggers = async () => { return { customTriggers: [ { _id: "d11eda27-4484-4eee-b2a9-13f72c49a277", name: "Happy Hour Custom Trigger" } ] } }; // Retrieve eligible triggers to apply export const getEligibleTriggers = async (options, context) => { var trigger = options.triggers.find(trigger => trigger.customTrigger._id === "d11eda27-4484-4eee-b2a9-13f72c49a277") const userCurrentDateTime = new Date; const userCurrentHour = userCurrentDateTime.getHours(); var result // Trigger is eligible if the time is between 4-6pm if (userCurrentHour >= 16 && userCurrentHour <= 18) { result = { eligibleTriggers: [ { customTriggerId : "d11eda27-4484-4eee-b2a9-13f72c49a277", identifier: trigger.identifier } ] } } else { result = { eligibleTriggers: [] } } return result }; ``` **Lines 1-4**: First we import the eCom custom triggers module. Then we export the `listTriggers()` function where we define any custom triggers we want to create. **Lines 6-11**: We return an array of `customTriggers`, giving each one an `_id` and a `name`. **Line 16**: Then we export the `getEligibleTriggers()` function where we define the custom logic to determine whether or not to apply the custom trigger. **Lines 18-21**: We declare our variables. **Line 24**: We check if the current time is between 4-6pm. **Lines 26-31**: If so, then the trigger we created in `listTriggers()` function is eligible and should trigger the applicable discount. **Lines 33-37**: If not, then `eligibleTriggers` is an empty array as there are no eligible triggers to apply. ### Optional: Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` + **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . 2. Select the **New.js** file. ## Step 3: Create a new discount and assign the custom trigger Create an automatic discount with the [Discount Rules API](https://dev.wix.com/docs/velo/api-reference/wix-ecom-backend/discount-rules/create-discount-rule.md), or by [using the dashboard](https://support.wix.com/en/article/wix-stores-creating-automatic-discounts) and set the custom trigger using the `name` of the custom trigger we created in our `listTriggers()` function. In our example, `"Happy Hour Custom Trigger"`. ### Optional: Test the plugin Once the discount is created and connected to your custom trigger, you can test your plugin before publishing your site in the Wix Editor using [functional testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) like you would with any backend Velo code. Make sure your `getEligibleTriggers()` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in the [Site Events log](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 4: Deploy the plugin When your code files are ready, you can publish your site and then try it out for yourself to see the custom discount trigger in action. 1. Once your code files are ready, click **Save**. Then publish your site. 2. After your service plugins are published, visit your site. If you visit your site between 4-6pm you should see the 25% discount applied to all menu items. ## Remove the plugin To remove the plugin from your site, follow these steps: + **Wix Studio:** Right-click on the plugin's folder and select **Delete Permanently**. + **Wix Editor:** 1. In the **Public & Backend** section of the Velo sidebar, under **Service Plugins**, hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/c4ff84cb-4d96-4e1a-966a-9c6b66dca0b2/062297c2-c368-4aee-b705-f0a55abc5bf6.png) . 2. Select **Remove**. 3. Click **Remove**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: eCommerce Shipping Rates Service Plugin ## Article: eCommerce Shipping Rates Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/e-commerce-shipping-rates-service-plugin.md ## Article Content: # Velo Tutorial: eCommerce Shipping Rates Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to expand what your site can do by integrating with 3rd-party services not currently supported by Wix. They also allow you to implement custom logic to change how your site displays and behaves. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md). With a Shipping Rates service plugin, you can connect your site to shipping rates providers not currently supported by Wix. You can also implement custom shipping rates and options using code. These rates are displayed on your store's **Cart** and **Checkout** pages.  This guide explains how to set up a shipping rates plugin on your site using Velo. The process has 3 steps: 1. Create a new shipping rates plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. >**Note** > This feature is still in beta testing and may change significantly before the final release. Elements that are not fully functional are noted below. ## Step 1: Create a new shipping rates plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or the **/src/backend/__spi__** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. [Add the Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) to your site. 2. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Code sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Public & Backend**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png) , then click **Shipping Rates**. 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/__spi__/ecom-shipping-rates**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Code sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio). The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, `.js` and `-config.js`. Implement the custom code for your plugin in these files. Here are some guidelines for writing your code: ### `.js` The code in this file defines a function named `getShippingRates()`. This function is called by Wix eCommerce to retrieve the shipping rates provided by your plugin. The function accepts the following parameter: `options`: An object containing data about line items, the shipping destination, the shipping origin, and general configurations. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-shipping-rates). Examples options object: ```json { "lineItems": [ { "name": "toy plane", "quantity": 1, "catalogReference": { "appId": "215238eb-22a5-4c36-9e7b-e7c08025e04e", "catalogItemId": "1a2d7e83-4bef-31d5-09e1-3326ee271c09" }, "physicalProperties": { "sku": "0002", "shippable": true, "weight": 1 }, "price": "200", "priceBeforeDiscount": "250", "totalPrice": "200", "totalPriceBeforeDiscount": "250" } ], "shippingDestination": { "addressLine1": "34 Elizabeth Street", "addressLine2": "4th floor", "city": "Melbourne", "country": "AU", "postalCode": "3000", "subdivision": "VIC" }, "shippingOrigin": { "addressLine1": "235 West 23rd Street", "addressLine2": "3rd floor", "city": "New York", "country": "US", "postalCode": "10011", "subdivision": "US-NY" }, "buyerDetails": {}, "weightUnit": "KG" } ``` >**Notes** > * Item objects don't include the physical dimensions of the products in your store. You might need to include these to retrieve 3rd-party shipping rates. In this case, store them in a separate collection together with a unique identifier such as an SKU. You can retrieve the dimensions from this collection in your service plugin code using the [wix-data API](https://www.wix.com/velo/reference/wix-data) and include them in your 3rd-party API call. > * The value of shippingOrigin is based on the business address set on your site. To set a business address open your site’s [dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Go%20to%20Dashboard&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/home/) and go to **Settings > Business Info > Location & Contact Info > Address**. The `getShippingRates()` function must return an object with a key called `shippingRates`. The value of this key is an array of `shippingRate` objects. These objects define the shipping rate options that site visitors see on the **Cart** and **Checkout** pages. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-shipping-rates). Example return value: ```json { "shippingRates": [{ "code": "123456-abcdef", // A unique identifier "title": "USPS - International", "cost": { "price": "15", "currency": "EUR", "additionalCharges": [{ "price": "12", "details": "Shipping surcharges and handling fees" }] }, "logistics": { "deliveryTime": "2-5 days", "instructions": "Please be available" } }] } ``` >**Note** > The currency property of each `shippingRate` object must match the site's currency. Shipping rates with other currencies are not displayed on the site. Include any currency conversions in your implementation. ### `-config.js` The code in this file defines a function named `getConfig()` that returns an object containing the values used to display the extended shipping rate on your site's dashboard. Example return object: ```json { "name": "best_shipping_rates_provider" "description": "Shipping rates for the US east-coast" } ``` >**Note** > Displaying the details provided by `getConfig()` is not supported in beta testing. For the purposes of testing, use a dummy value for your `getConfig()` return object, such as the one above. ### Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . 2. Select the **New.js** file. + **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ### Test the plugin You can test your plugin before publishing your site in the Wix Editor using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) like you would with any backend Velo code. Make sure your `getShippingRates` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). ## Step 3: Deploy the plugin Once your code files are ready, you need to deploy your plugin and enable it on your site's dashboard. 1. Publish your site. > **Note:** > There may be a delay between publishing the site and the new shipping rates options appearing on your site's dashboard. 2. Go to the [Shipping & Fulfillment](https://www.wix.com/my-account/site-selector/?buttonText=Manage%20Shipping&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7B%7BmetaSiteId%7D%7D/store/shipping-and-tax/list?referralInfo=sidebar) settings on your site's dashboard. 3. Click the region where you want to apply the new shipping rates. 4. Under **Installed Apps** click the toggle for your new shipping rates plugin. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/04/14/a46bda1b-a8b2-47f4-8de0-499e862f9c10/517b4381-0385-4d7c-9de7-73ee3a72c817.png) > **Note:** > The names of the shipping rates options don't appear in the beta version. The different options available are listed based on the order in which you add them to your site. 5. Click **Save**.   6. Once your service plugin is deployed, your custom shipping rates appear on your site's **Cart** and **Checkout** pages. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/6594569d-aead-47ff-beb5-eb7e9076fe0c/1b7d35f4-fb86-4b3b-8761-730d5cc7fc82.png) ## Examples Service plugins allow you to implement shipping rates with custom logic and display them on your site. We created some examples to demonstrate how you can use service plugins to extend the shipping rates options on your site. > **Note:** These examples and their steps are based on Wix Editor sites. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md). ### Display rates by expected delivery In this example, shipping options for a variety of shipping times are displayed. To test this code, paste it into your **.js** file. ```javascript export const getShippingRates = async (options) => { return { shippingRates: [{ title: 'Same day delivery', code: 'one_day_rush', logistics: { deliveryTime: 'Today', instructions: 'Please be available', }, cost: { price: "20.00", currency: 'EUR', additionalCharges: [], } }, { title: 'Next Day', code: 'next-day', logistics: { deliveryTime: 'Tomorrow', instructions: 'Please be available', }, cost: { price: '15.00', currency: 'EUR', additionalCharges: [], } }, { title: '2 Days', code: 'two_days', logistics: { deliveryTime: '2 Days', instructions: 'Please be available', }, cost: { price: '12.00', currency: 'EUR', additionalCharges: [], } }] } }; ``` ### Display rates based on product and cart data In this example, product weight and quantity data is used to determine shipping rates. The plugin includes 2 helper files. One, `rates-by-weight`, includes code that calculates a shipping rate based on the total weight of the shipment. The second file, `rates-by-quantity`, calculates a shipping rate based on the total number of items being shipped. Both the weight and quantity values are calculated using the product data that’s passed into the plugin's `getShippingRates` function. The code in the example’s `getShippingRates` function calculates the rates for shipping by weight or by quantity and returns the lower of the two. We built a [sample site](https://manage.wix.com/edit-template/from?originTemplateId=dec2dc1f-740e-42a3-bf1e-625c2df3c4f1&templateId=f17e3efd-2877-47c2-b202-3c9f2641ba14&autoDevMode=true) where you can see this code in action. >**Note:** > Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. ### 3rd-party plugin We built [a sample site](https://manage.wix.com/edit-template/from?originTemplateId=103d0e2e-46f3-42ec-928c-f61a328495c3&templateId=9e9f7487-d7a5-4548-b4a4-65fedbf08199&autoDevMode=true) that demonstrates 3rd-party integrations. This site integrates with shipping rates provider [Easyship](http://www.easyship.com) and displays the provided rates on the site. >**Note:** > Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. To get the example working you need to get an easyship API key, store the key in a secret on your copy of the sample site, and add a business address to the site: #### Step 1: Retrieve and save an easyship API key 1. Publish a copy of the template site to save it to your Wix account. 2. Sign up for a free [easyship](http://www.easyship.com/) account. 3. On your account dashboard click **Create an Api**. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/2723858b-0fe5-48d2-b16a-52069273d078/1f878ea7-5319-4ea7-a0c4-5ac42ccf4c5e.png) 4. Enter a name for your API integration and click **Connect**. 5. Copy the production API key. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/f556c4a1-3a8c-4652-89c3-4febf0c23ca1/2fc42273-179d-479f-b817-a2d0cd514a40.png) 6. Open your site’s [**Secrets Manager**](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) and create a new secret named \`easy-ship-api-token\` with your easyship API key as the value. #### Step 2: Add a business address to your site 1. On your site’s dashboard, go to **Settings > Business Info > Location & Contact Info > Address**. 2. Add a business address for the site. 3. Click **Save**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Validations Service Plugin ## Article: Tutorial: Validations Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-validations-service-plugin.md ## Article Content: # Tutorial: Validations Service Plugin Wix Service Plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site behaves and displays. For example, when you set up a Wix eCommerce site, there are a small number of validations to choose from. The validations service plugin allows you to implement your own validations and options using code. You can also connect your site to validation providers not currently supported by Wix. Any violations to these validations are displayed on your store's Checkout and Cart pages. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md). With the Validations service plugin you can define the validations for a cart or checkout that fit your site's needs. Possible validations include: - Minimum subtotal amount to qualify for free shipping. - Age of a customer before they proceed to checkout. - Specific items to ship only to specific regions. - Restrict purchases to site members only. - Close the checkout on certain days. ## Introduction This tutorial explains how to set up and implement a validation plugin on your site using Velo. We created an example to demonstrate how you can use service plugins to validate your checkout and cart pages. We use the example of a wholesale store where a customer must purchase a minimum total of 300 items, and if a customer purchases toothbrushes, then they must purchase at least 200 toothbrushes. The process has 3 steps: 1. Create a new validations plugin on our site. 2. Implement our plugin with custom code. 3. Deploy the plugin. ## Step 1: Create a new validation plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or the **/src/backend/**spi\*\*\*\* section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. [Add the Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) to your site. 2. Enable coding: - **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. - **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Code sidebar. 3. Go to the Service Plugins section: - **Wix Studio:** Click **Public & Backend**. - **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Next to **Service Plugins**, click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png), then click **eCommerce Validation**. (In Wix Editor you need to hover over **Service Plugins**.) 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/**spi**/ecom-validations**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Code sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio). The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, **minimums.js** and **minimums-config.js**. We implement the custom code for our plugin in these files. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3b636fad10dac2f141d6e756b43b9bf8.png) ## minimum-config.js This file is where write the code for setting up the plugin's configuration. The code in this file defines a function named `getConfig()` that returns an object containing the values used to display the eCommerce validations on your site's dashboard. By default, the Validations service plugin only validates a site visitor's checkout. In our example, we want to also validate a site visitor's cart, so we set the `validateInCart` parameter to `true` in the service plugin's configuration file. ```js import * as ecomValidations from "interfaces-ecommerce-v1-validations-provider"; export function getConfig() { return { validateInCart: true }; } ``` ## minimum.js This file is where we write the code for validating the minimum total item quantity, and the minimum quantity of toothbrushes (a line item's quantity). A customer must purchase a total of at least 300 items, and if the customer is purchasing toothbrushes, they must purchase at least 200 toothbrushes. If there are any violations to these validations, they are shown in a site visitor's cart and checkout pages(see images in step 3). The code in this file defines a function named `getValidationViolations()`. This function is called by Wix eCommerce to retrieve any violations to the validations provided by our plugin. The function accepts the following parameter: `options`: An object containing data about the source of the request, and the cart or checkout information to validate. This information includes line items, gift cards, applied discounts, shipping information, and billing information. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations). The `getValidationViolations()` function returns an array of validation objects (any validation violations in a site visitor's cart or checkout). These validation violations are displayed in the site visitor's cart and checkout pages. If there are no validation violations, the response is an object containing an empty list. ## The code: ```js import * as ecomValidations from "interfaces-ecommerce-v1-validations-provider"; export const getValidationViolations = async (options, context) => { let violations = []; const source = options.sourceInfo.source; const severity = source == ecomValidations.Source.CART ? ecomValidations.Severity.WARNING : ecomValidations.Severity.ERROR; const bambooDescription = "You must purchase at least 200 Bamboo Toothbrushes."; const bambooLineItem = options.validationInfo.lineItems.find((lineItem) => isBambooTooth(lineItem.catalogReference?.catalogItemId) ); if (bambooLineItem != undefined) { const quantity = bambooLineItem.quantity; if (quantity > 0 && quantity < 200) { violations.push({ severity, target: { lineItem: { name: ecomValidations.NameInLineItem.LINE_ITEM_DEFAULT, _id: bambooLineItem._id, }, }, description: bambooDescription, }); } } const cartQuantityDescription = "You must purchase a minimum of 300 items."; const cartQuantity = options.validationInfo.lineItems.reduce( (partialSum, lineItem) => partialSum + lineItem.quantity, 0 ); if (cartQuantity < 300) { violations.push({ severity, target: { other: { name: ecomValidations.NameInOther.OTHER_DEFAULT } }, description: cartQuantityDescription, }); } return { violations }; }; const isBambooTooth = (catalogItemId) => { const bambooToothCatalogItemId = "cd59cd36-b6d2-2cf3-9d48-81793a7bdbbd"; const ifBambooTooth = catalogItemId != undefined && catalogItemId == bambooToothCatalogItemId; return ifBambooTooth; }; ``` **Line 1-3**: First we import the eCom validations provider module. Then we export the `getValidationViolations()` function where we set the custom logic for our validations. **Lines 4-5**: We declare our variables. **Line 6**: For the severity, if the source of the validation is a `CART`, then we want any violations to the cart to display as a warning. Otherwise (if the source of the validation is a checkout), then we want any violations to the checkout to display as an error. **Line 7**: Set the violation description that will be displayed when there is a violation to the toothbrush validation. **Lines 9-11**: Check the line items in the cart or checkout to see if they contain a toothbrush. We do this by calling a function that we create later in our code to check whether the catalog ID of the toothbrush in the cart or checkout matches the catalog item ID in the list of catalog items. If it matches, get the toothbrush's quantity. **Lines 12-20**: If the toothbrush's quantity is between 1-200, set the severity, target, and description of the violation and push them into a violations array. The target is set to the line item's default since the validation violation is on a specific line item. **Line 21**: Set the violation description that will be displayed when there is a violation to the minimum total item quantity validation. **Lines 22- 32**: Calculate the cart's total quantity. If the quantity is less than 300, set the severity, target, and description of the violation and push them into a violations array. The target is set to the other (general) validation violation default since the validation violation is not on a specific line item, rather on the total item quantity of the cart checkout. **Lines 33- 37**: Here we create a function to check whether the ID we pass into the function (the catalog ID of the toothbrush in the cart or checkout) matches the catalog item ID in the list of catalog items. ## Optional: Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. - **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . 2. Select the **New.js** file. - **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ## Optional: Test the plugin You can test your plugin before publishing your site in the Wix Editor using [functional testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) like you would with any backend Velo code. Make sure your `getValidationViolations()` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 3: Deploy the plugin Once your code files are ready, publish your site. Navigate to your site, and add an item to your Cart or Checkout. Any violations to the validations you've customized are displayed on the checkout or the cart pages. 1. Once your code files are ready, click **Save**. Then Publish your site. 2. After your service plugins are published, open your site. 3. Your service plugin is then deployed, and any validation violations should appear on your site's **Checkout** and **Cart** pages. If a customer adds only 20 Bamboo Toothbrushes to their cart. They will receive the following warnings displayed in their cart. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b4ae9703-4364-4550-a797-1cc95ed8046e/2023/07/02/bc4371d4-0736-42f8-b5ba-fb2b5a9b6be3/f428b826-c1e0-463d-b588-8330711b58fa.png) The validation violation response for the cart will look like this: ```js { "violations": [{ "severity": "WARNING", "target": { "lineItem": { "_id": "00000000-0000-0000-0000-000000000001" "name": "LINE_ITEM_DEFAULT" } }, "description": "You must purchase at least 200 Bamboo Toothbrushes." }, { "severity": "WARNING", "target": { "other": { "name": "OTHER_DEFAULT" } }, "description": "You must purchase a minimum of 300 items." }] } ``` If the same customer from above proceeds to the checkout page, they will see the following errors displayed at the top of the checkout page: ![Checkout violations - top](https://wixmp-833713b177cebf373f611808.wixmp.com/images/301fbadd7fe7e9df8ddcdd126d0812b3.png) And again at the button of the page. The button is disabled until the errors are resolved: ![Checkout violations - bottom](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a2168d9ffd289b9ef48df76e0cef0016.png) The validation violation response for the checkout will look like this: ```js { "violations": [{ "severity": "ERROR", "target": { "lineItem": { "name": "LINE_ITEM_DEFAULT" } }, "description": "You must purchase at least 200 Bamboo Toothbrushes." }, { "severity": "ERROR", "target": { "other": { "name": "OTHER_DEFAULT" } }, "description": "You must purchase a minimum of 300 items." } ] } ``` > **Note:** > > By default, the Validations service plugin only validates a site visitor's checkout. If you want to also validate a site visitor's cart, set the `validateInCart` parameter to `true` in the service plugin's configuration file. ## Remove the plugin You can remove the plugin from your site. Do the following: **Wix Editor:** 1. In the **Public & Backend** section of the Code sidebar, under **Service Plugins**, hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/c4ff84cb-4d96-4e1a-966a-9c6b66dca0b2/062297c2-c368-4aee-b705-f0a55abc5bf6.png) . 2. Select **Remove**. 3. Click **Remove**. **Wix Studio:** Right-click on the plugin's folder and select **Delete Permanently**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: eCommerce Recommendations Service Plugin ## Article: Tutorial: eCommerce Recommendations Service Plugin ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-e-commerce-stores/tutorial-e-commerce-recommendations-service-plugin.md ## Article Content: # Tutorial: Recommendations Service Plugin Wix service plugins (formerly SPIs and custom extensions) allow you to expand what your site can do by integrating with 3rd-party services not currently supported by Wix. They also allow you to implement custom logic to change how your site displays and behaves. Read more information on how to implement a [service plugin](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md). With the Recommendations Service Plugin, your service can seamlessly integrate with Wix to provide advanced recommendation algorithms, helping site owners deliver personalized suggestions that enhance user engagement and overall experience. This guide explains how to set up a recommendations plugin on your site. The process has 3 steps: 1. Create a new recommendations plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. ## Step 1: Create a new recommendations plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar (Wix Editor), or the **/src/backend/__spi__** section of the Wix IDE Explorer (Wix Studio), which contains the files for your code. 1. [Add the Wix Stores app](https://support.wix.com/en/article/wix-stores-adding-wix-stores) to your site. 2. [Add a Related Products gallery](https://support.wix.com/en/article/wix-stores-adding-a-related-products-gallery) to any page where you would like to display recommendations. On the product page, you can recommend related products. On any other page, you can display generic recommendations for the store or current visitor. 2. Enable coding: + **Wix Studio:** If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. + **Wix Editor:** Enable [Velo Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site), and then click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/08/29/23d50509-633d-4616-9f56-65add8fa6d0b/d910e041-d150-4f35-aa75-30ad262abc7a.png) tab in the Code sidebar. 3. Go to the Service Plugins section: + **Wix Studio:** Click **Backend & Public**. + **Wix Editor:** Scroll down to the Service Plugins panel at the bottom of the sidebar. 4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png), then click **Item recommendations**. 5. Follow the prompts to add the plugin and accept any terms and conditions that display. 6. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 7. **Wix Studio:** Open the Wix IDE, and go to **/src/backend/___spi___/ecom-recommendations-provider**. If your service plugin doesn't appear, try refreshing both the Studio Editor and the Wix IDE. 8. **Publish** your site. ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Code sidebar (Wix Editor), or in the Wix IDE's Explorer (Wix Studio). The name of the folder is based on the plugin you chose. Inside, there is another folder with the name of the plugin you set up. This folder contains 2 files, `.js` and `-config.js`. Implement the custom code for your plugin in these files. Here are some guidelines for writing your code: ### `-config.js` The code in this file defines a function named `getConfig()` that returns an object containing the values used to display the recommendations algorithms in the Related Products gallery settings in the editor. By configuring the Related Products category, you can select which algorithms to get items from. Wix sends requests for those algorithms when the Recommended Products gallery loads. Example of a valid config: ```json { "catalogAppIds": ["215238eb-22a5-4c36-9e7b-e7c08025e04e"], "supportedAlgorithms": [ { "algorithmId": "945a457f-0a57-4cf4-95a1-95991b545119", "name": "In stock from the same category", "description": "If selected product is out of stock, suggest in-stock products from the same category.", "additionalInfo": "Works best on product page.", "algorithmType": "RELATED_ITEMS" }, { "algorithmId": "97f48eb2-4d42-4296-b620-13862dd0353e", "name": "Cheapest in the store", "description": "The cheapest products in the store.", "algorithmType": "GLOBAL" } ] } ``` ### `.js` The code in this file defines a function named `getRecommendations()`. This function is called by Wix to retrieve the recommended items provided by your plugin. The function accepts the following parameter: `options`: An object that includes the `algorithmId` of the selected algorithm. If the Related Products gallery is installed on a products page, the `items` field will be populated with the product ID as `catalogItemId` and the Stores App ID as `appId`. On any other page, the `items` field will be empty. Example of options object: ```json { "algorithmIds": ["945a457f-0a57-4cf4-95a1-95991b545119"], "items": [ { "catalogItemId": "ec7914e8-208b-0069-5850-1a965643508d", "appId": "215238eb-22a5-4c36-9e7b-e7c08025e04e" } ] } ``` The `getRecommendations()` function must return an object with a key called `recommendationPerAlgorithm`. The value of this key is an array of recommended items. Example of returned value: ```json { "recommendationPerAlgorithm": [ { "algorithmId": "945a457f-0a57-4cf4-95a1-95991b545119", "recommendedItems": [ { "catalogItemId": "1ce6198f-5fad-5ac9-5b36-ecb2175929ef", "appId": "215238eb-22a5-4c36-9e7b-e7c08025e04e" }, { "catalogItemId": "58a7ab15-5c65-d836-7934-86f78b17996b", "appId": "215238eb-22a5-4c36-9e7b-e7c08025e04e" } ] } ] } ``` >**Note**: `catalogItemId` must be a valid product ID from the store app. ### Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. + **Wix Editor:** 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) . 2. Select the **New.js** file. + **Wix Studio:** Create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ### Test the plugin You can test your plugin before publishing your site in the Wix Editor using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) like you would with any backend site code. Make sure your `getRecommendations` function's return values are properly formatted. You can test your plugin after deploying for both Wix Editor and Wix Studio. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md). ## Step 3: Deploy the plugin Once your code files are ready, you need to deploy your plugin and enable it on your site's dashboard. 1. Publish your site. 2. Go to the page with the Related Products gallery. 3. Click "Settings" on the gallery. Your recommendation algorithms appear in the dropdown, alongside other algorithms provided by the Stores app. Algorithms with the `algorithmType` of `RELATED_ITEMS` are listed under the "Related Products" section. 4. Select the algorithm you want to use. The recommended products will appear in the gallery. ## Examples ### Cheapest products in the store You can return the top 10 cheapest products in the Store Products Wix collection. ```javascript import wixData from 'wix-data'; /** * Returns lists of items recommended by each requested algorithm. * * If the algorithm's type is `RELATED_ITEMS` then the `items` field is required in the request, and the response will contain items related to those submitted. * How the related items are selected based on the requested items depends on the algorithm. Some examples include items related by category or those frequently bought or watched together. * @param {import('interfaces-ecom-v1-recommendations-provider').GetRecommendationsOptions} options * @param {import('interfaces-ecom-v1-recommendations-provider').Context} context * @returns {Promise} */ export const getRecommendations = async (options, context) => { const recommendations = await Promise.all(options.algorithmIds.map((algorithmId) => { if (algorithmId === "97f48eb2-4d42-4296-b620-13862dd0353e") { return wixData.query("Stores/Products") .ascending("price") .limit(10) .find() .then((results) => { return { algorithmId: algorithmId, recommendedItems: results.items.map((item) => { return { catalogItemId: item._id, appId: "215238eb-22a5-4c36-9e7b-e7c08025e04e" } }) } }); } })); return { "recommendationPerAlgorithm": recommendations } }; ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating Bookings Resources Using Velo ## Article: Creating Bookings Resources using Velo ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/creating-bookings-resources-using-velo.md ## Article Content: # Creating Bookings Resources using Velo In Wix Bookings, resources are anything you need to provide a service. It can be a staff member, a room, or a piece of equipment. This tutorial focuses on creating staff resources, but apart from the “staff” tag, there is very little difference between types of resources. For the first part of this tutorial, we’ll create a staff resource with availability during the business's appointment hours. In the second part, we’ll create a staff resource with its own set of available hours. The available hours for a resource are the hours during which they can be booked for a service. A resource is a simple object that has an ID, basic contact details, its schedule ID and a tags array. You can use the tags array for grouping resource types. The Bookings app uses the tag "staff" to mark the resource as a staff member. This will make it show up in the [**Staff**](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fbookings/staff) section of the Bookings app in the dashboard. The Bookings app creates a special resource with "business" in the tag and name properties. This special resource has a schedule that defines the [business's appointment hours](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fbookings/scheduler/owner/calendar-settings). Use the business resource's schedule to set up a staff or other resource's available hours to match the business's appointment hours. A resource object looks like this: ```json { "_id": "6235c808-c3c6-49f2-a000-bc8eded9f106", "name": "John Doe", "email": "john@doe.com", "phone": "555 4567", "description": "Fitness Instructor", "tags": [ "staff" ], "scheduleIds": [ "a5d20a3a-eb25-497a-9fc1-f546ac8422fc" ], "status": "CREATED" } ``` Resources have schedules, which define when the resource is available. A basic resource schedule looks like this: ```json { "_id": "a5d20a3a-eb25-497a-9fc1-f546ac8422fc", "scheduleOwnerId": "6235c808-c3c6-49f2-a000-bc8eded9f106", "availability": { "linkedSchedules": [ "scheduleId": "48ed1477-443b-40f4-bd71-7a04e863cafd" ], "start": "Mon May 01 2021 09:00:00" } } ``` * `_id` is the unique schedule ID. * `scheduleOwnerID` is the ID of the entity that owns the schedule, in this case a resource. * The `availability` object has the `start` time from which the schedule begins, and an array of linked schedules. * The `linkedSchedules` array contains a list of other schedules that add to this schedule’s availability. In this case we have the business's appointment hours that are stored in the bookings app as a schedule. By linking the business's appointment hours schedule to this resource’s schedule we are making the resource available during the business's appointment hours. Not all schedules have linked schedules. You can define custom hours for a resource by creating sessions. More on sessions later. ### Install the Bookings App Before you try the code below, make sure to install the Bookings app if you haven't already. You can learn more about installing apps [here](https://support.wix.com/en/article/studio-editor-adding-apps) (Wix Studio) or [here](https://support.wix.com/en/article/adding-an-app-to-your-site) (Wix Editor). ### Creating a Staff Resource that uses the Business's Appointment Hours To create a staff resource that uses the business's appointment hours, we need to collect the following information: * The resource’s name, email, and phone number. * The business schedule ID. For resource details, let's assume that we have a page containing a form. The form has fields for resource name, email, phone number, and a brief description. The form also has  a submit button that calls our backend function to create a resource, and passes the form details in an object. The following frontend page code collects the input data from the form when the submit button is clicked and calls the createStaffWithBusinessHours function defined in the backend file "resources.web.js" that we will create. ```javascript import { createStaffWithBusinessHours } from 'backend/resources.web' export function buttonSubmit_click(event) { const resourceInfo = { name: $w('#inputName').value, email: $w('#inputEmail').value, phone: $w('#inputPhone').value, description: $w('#textBoxDescription').value } createStaffWithBusinessHours(resourceInfo) .then((resource) => { console.log("Resource:", resource); }) .catch((error) => { console.error("Failed to create the resource:", error) }); } ``` Create a file called resources.web.js in the backend file section of your site.  This file will contain all of the backend code used in this tutorial. The code below has 2 functions: * Get the business resource's schedule ID.  * Create the resource. The business's scheduleId is the ID of the schedule that contains the business's appointment hours. These hours can be set in the dashboard under Settings, Bookings, [Appointment Hours](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/bookings/scheduler/owner/bookings-options/calendar-settings).   To create a resource that uses the business's appointment hours, add the business resource's  `scheduleId` to the `linkedSchedules` array of the new resource's schedule. ```javascript import { Permissions, webMethod } from "wix-web-module"; import { resources } from "wix-bookings-backend"; // Get the business resource's schedule that contains the business's appointment hours. export const getBusinessSchedule = webMethod(Permissions.Anyone, () => { return resources .queryResourceCatalog() .eq("slugs.name", "business") .find() .then((results) => { const businessResource = results.items[0].resource; const businessResourceScheduleId = businessResource.scheduleIds[0]; return businessResourceScheduleId; }); }); // Get the schedule ID for the business resource. export const createStaffWithBusinessHours = webMethod(Permissions.Anyone, async (staffInfo) => { const businessResourceScheduleId = await getBusinessSchedule(); // Add the business schedule ID to the linkedSchedules array. const scheduleInfo = [ { availability: { linkedSchedules: [ { scheduleId: businessResourceScheduleId, }, ], start: new Date(), }, }, ]; const resourceInfo = { name: staffInfo.name, email: staffInfo.email, phone: staffInfo.phone, description: staffInfo.description, tags: ["staff"], }; const options = { suppressAuth: true }; // Create the resource and return the result. return resources .createResource(resourceInfo, scheduleInfo, options) .then((resource) => { return resource; }) .catch((error) => { console.error("Failed to create resource: ", error); return error; }); }); ``` #### Understanding the code. **Get the business resource's schedule ID.** **Line 1:** Import `Permissions` enum and `webMethod` function from `wix-web-module`. **Line 2:** Import the resource APIs from the `wix-bookings-backend module`. **Line 7:** Using `queryResourceCatalog` get the resource where the slugs name is "business". **Line 11:** The query returns an array of items, each item containing a resource, schedule and slug object. We want the resource object from the first and only item in the array. **Line 12:** From the resource, take the first ID in the `scheduleIds` array. **Line 13:** Return the schedule ID to the calling function. **Create the resource** **Line 19:** Call `getBusinessSchedule()` to get the schedule ID for the business's appointment hours. **Line 22:** Create the `scheduleInfo` object using the business schedule ID in the `linkedSchedules` array. This gives the resource the same available hours as defined for the business. **Line 30:** Set the date for this schedule to start, in this case immediately. **Line 34:** Create the `resourceInfo` object using the values from the front-end form, and adding the tag `"staff"` to the `tags` array. **Line 42:** Configure the options object to set `supressAuth` to `true`. This will suppress the checking of user permissions and let anyone execute the `createResource` function. **Line 45:** Call `createResource` using the `resourceInfo`, `scheduleInfo` and `options` objects. **Line 47:** If the promise resolves, return the created resource to the front end. **Line 50:** If the promise is rejected, log the error to the console, and return it to the front end. [See if it worked](https://support.wix.com/en/#check-that-it-worked) by looking at your site's dashboard. ### Creating a Resource with Custom Hours In this part of the tutorial, we will create a resource that has its own custom working hours. We will make the resource available on Mondays, Wednesdays, and Fridays between 10 AM and 4 PM.  To create resource with its own custom hours, we need to do the following: 1. Create a resource and its schedule. 2. Add sessions to the resource's schedule to define its availability. A [session](https://www.wix.com/velo/reference/wix-bookings-backend/sessions/introduction) is a defined period of time on a schedule and is one of the following: * `"EVENT"`: Reserved period of time on any [schedule](https://www.wix.com/velo/reference/wix-bookings-backend/sessions/introduction#wix-bookings-backend_sessions_introduction_schedules). For example, an appointment, class, or course. Events are visible in the Dashboard on the Bookings app's [**Booking Calendar**](https://support.wix.com/en/article/wix-bookings-about-the-wix-bookings-calendar) page. * `"WORKING_HOURS"` : Placeholder for available time on a resource’s schedule. In this tutorial we will create "`WORKING_HOURS"` sessions to define the resource's availability. A session can be an individual session or a recurring session. An individual session has a discrete start and end date, while a recurring session defines a series of repeating sessions. In this tutorial, when adding the sessions to the schedule, we will create recurring sessions using recurrence rules to specify how often each session repeats. **Create a function to manage the process.** First we need a function that will manage the process for us. This function will call the `createStaffMember` function to create the resource, then use the new resource's `scheduleId` to add sessions to the schedule. ```javascript import { Permissions, webMethod } from "wix-web-module"; import { resources, sessions } from "wix-bookings-backend"; // A function that creates a resource and adds recurring sessions to its schedule. export const createStaffWithCustomHours = webMethod(Permissions.Anyone, async (staffInfo) => { // Create a resource with no linked schedules. try { var resource = await createStaffMember(staffInfo); } catch (error) { console.error("Failed to create a resource", error); return error; } // Using the schedule ID of the resource created above, create recurring sessions. return createRecurringSessions(resource.scheduleIds[0]) .then((sessions) => { //create an object with the resource and a list of sessions. const resourceSessions = { resource: resource, sessions: sessions, }; return resourceSessions; }) .catch((error) => { console.error("Failed to create a session", error); return error; }); }); ``` #### Understanding the code. **Line 1:** Import `Permissions` enum and `webMethod` function from `wix-web-module`. **Line 2:** Import both resources and sessions from `wix-bookings-backend module`. **Line 5:** `createStaffWithCustomHours` takes staff information from the page form, and creates a resource. It then uses the new resource’s `scheduleId` to create recurring sessions. **Line 8:** Call `createStaffMember` to create a new resource. Note that the resource variable is defined with `var` so that it is visible outside the try/catch scope. **Line 16:** Call `createRecurringSessions` using the resource’s scheduleId. **Line 19:** Create `resourceSessions` as a return object consisting of the resource and an array of its sessions. **Line 23:** Return the `resourceSessions` object to the front end. #### Create the resource Creating the resource is the same as creating a resource with the business's appointment hours, but in this case we leave out the business schedule from the linkedSchedules array in the scheduleInfo object. ```javascript // A function that creates staff resources with no linkedShedules in its schedule. export const createStaffMember = webMethod(Permissions.Anyone, async (staffInfo) => { // Create resource information with a "staff" tag. const resourceInfo = { name: staffInfo.name, email: staffInfo.email, phone: staffInfo.phone, description: staffInfo.description, tags: ['staff'], }; // Schedule information with no linked schedules. const scheduleInfo = [{ availability: { linkedSchedules: [], start: new Date(), } }]; // Set the option to suppress permissions checking. const options = { suppressAuth: true } // Create the resource and return it to the calling function. return resources.createResource(resourceInfo, scheduleInfo, options) .then((resource) => { return resource; }) .catch((error) => { console.error('Failed to create resource: ', error); return error; }); }); ``` #### Understanding the code. **Line 5:** Create the `resourceInfo` object using the values from the front-end form, adding the tag `"staff"` to the tags array. **Line 14:** Create the `scheduleInfo` object with an empty `linkedSchedules` array and the current date and time for the `start` date. **Line 22:** Configure the `options` object to set `supressAuth` to `true`. This will suppress the checking of user permissions and let anyone execute the `createResource` function. **Line 25:** Call `createResource` using the `resourceInfo`, `scheduleInfo` and `options` objects and return the result to the calling function. #### Create recurring sessions. Now that we have a resource and its schedule, we can add sessions to the schedule.  We are going to add recurring sessions that occur on Mondays Wednesdays and Fridays between 10 AM and 4 PM. The recurrence rule for this as follows: `'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;UNTIL=20220101T000000Z'` * `FREQ=WEEKLY`: Repeat the sessions on a weekly basis * `INTERVAL=1`: Repeat the sessions every week. If you want to repeat every second week, set the interval to "2" * `BYDAY=MO`: The session occurs on Mondays. * `UNTIL=20220101T000000Z`: Repeat the sessions until midnight, January 1 2021 We will have 3 recurrence rules. One for Monday, `BYDAY=MO`, one for Wednesday, `BYDAY=WE`, and one for Friday, `BYDAY=FR.` The session type is set to `'WORKING_HOURS'` to tell the bookings backend that these sessions set the resource’s availability. ```javascript // A function that creates recurring sessions for a given schedule ID. export const createRecurringSessions = webMethod(Permissions.Anyone, async (resourceScheduleId) => { // Set the option to suppress permissions checking. const options = { suppressAuth: true }; // Create the recurring session object, leaving the recurrence rule to be populated later. let sessionInfo = { scheduleId: resourceScheduleId, start: { localDateTime: { year: 2021, monthOfYear: 5, dayOfMonth: 1, hourOfDay: 10, minutesOfHour: 0, }, }, end: { localDateTime: { year: 2021, monthOfYear: 5, dayOfMonth: 1, hourOfDay: 16, minutesOfHour: 0, }, }, // Set the session type to "WORKING_HOURS" to define the session as a resource availability session. type: "WORKING_HOURS", recurrence: "recurrence rule placeholder", }; let resourceSessions = []; try { // Populate the recurrence rule in the recurringSession object for Monday, Wednesday, and Friday, then call createSession for each rule. // Add each session the resourceSessions array sessionInfo.recurrence = "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;UNTIL=20220101T000000Z"; resourceSessions.push(await sessions.createSession(sessionInfo, options)); sessionInfo.recurrence = "FREQ=WEEKLY;INTERVAL=1;BYDAY=WE;UNTIL=20220101T000000Z"; resourceSessions.push(await sessions.createSession(sessionInfo, options)); sessionInfo.recurrence = "FREQ=WEEKLY;INTERVAL=1;BYDAY=FR;UNTIL=20220101T000000Z"; resourceSessions.push(await sessions.createSession(sessionInfo, options)); return resourceSessions; } catch (error) { console.error("Failed to create a session", error); return error; } }); ``` #### Understanding the code. **Line 8:** Create the sessionInfo object using the scheduleId from the resource we created earlier. **Lines 10 and 19:** Set the duration of the session by setting the `start.locatDateTime` and `end.localDateTime` properties. **Line 29:** Set the session type to `”WORKING_HOURS”`. This tells the bookings app that these sessions set the resource’s availability. **Line 30:** Define the `recurrence` rule as a placeholder. We will populate this property with a set of recurrence rules. **Line 33:** Declare an array to hold the sessions that we are going to create. **Line 37:** Set the recurrence rule for the first recurring session to `'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;UNTIL=20220101T000000Z'` The rule defines sessions that will recur on a weekly basis, on a Monday, repeating until midnight, January 1, 2021. **Line 38:** Call `createSession` with the `sessionInfo` and `options` objects, storing the result in the `resourceSessions` array that we created earlier. **Lines 40 to 44:** Repeat the session creation, changing the recurrence rule each time to repeat on Wednesdays, then Fridays. **Line 46:** Return the `resourceSessions` array, containing three recurring sessions, to the `createStaffWithCustomHours` function. ### Check that it worked. How do we check that this worked ? The easiest way is to go to the [Staff](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fbookings/staff) page of the Bookings app in your site's dashboard. You can also use the [queyResourceCatalog()](https://www.wix.com/velo/reference/wix-bookings-backend/resources/queryresourcecatalog) and use the resourceId  or the resource's scheduleId in [querySessions().](https://www.wix.com/velo/reference/wix-bookings-backend/sessions/querysessions) The Staff page in the dashboard. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/05/26/82aa4297-ae2e-47f7-9b20-99e3b725f830/9b592896-891e-4c5d-8ec5-19042b7b8f3a.png) ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/05/26/b6620360-9f10-4f7d-8dd6-77ff4c082182/b9bc21c1-bce4-44f4-aadd-d3d829ffe185.png)
Hover over a staff member and hit **edit** to see their available hours.
Click **Edit Staff Availability.** You can see if the staff member uses default or custom appointment hours. 
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Blocking Off Time in the Wix Bookings Calendar ## Article: Blocking Off Time in the Wix Bookings Calendar ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/blocking-off-time-in-the-wix-bookings-calendar.md ## Article Content: # Velo: Blocking Off Time in the Wix Bookings Calendar The Wix Bookings backend module lets you manage bookings resources using Velo APIs. When managing staff resource schedules, a common use case is setting up the times that staff are available, and the times that they're not. What if you want to block off some time in the middle of the work day when your staff resource is not available ? You can do this by using the `createSession()` function to create "blocked time".  To set up blocked time, you need to create a session of type `"EVENT"` with a tag value of `"Blocked"`. The session can be a [single session or a recurring session](https://www.wix.com/velo/reference/wix-bookings-backend/sessions/introduction#wix-bookings-backend_sessions_introduction_recurrence). In the first example, we'll create a function that takes a resource ID and creates a single blocked time session on the resource's schedule. We'll also see how to create a dropdown element containing all of your resources. Once we have that working, in the second example, we'll add some functionality to make recurring blocked time sessions. Let's assume you already have some staff members defined. If you don't, take a look at [Creating Bookings Resources using Velo](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/creating-bookings-resources-using-velo.md), or create some using the dashboard [Staff](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fbookings/staff) page.
Before we go on, you need to install the Bookings app. If you haven't installed it yet, here's how to do it. 1. Select the Add Apps icon in the editor.
2. Type "Bookings" in the search field and select the Wix Bookings App. 3. Click "Add to Site."
### Setting up our site page
Create a page with the elements shown above. Put the **Recurrence Rule** text and **inRecurrenceRule** input element inside the container box **box1**. In the Properties and Events panel for **box1**, select **Collapsed** under **Default Values**. Set the **default text** as shown above for the **From**, **To**, and **Recurrence Rule** input elements. >**Timestamps and Recurrence Rules.** > Make sure that the format of the timestamps and recurrence rule are correct. To reduce complexity, we are not going to validate them in this tutorial. > > Timestamp example: `2021-06-15T13:00:00.000-05:00`  > > Recurrence Rule example: `FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;UNTIL=20220101T000000Z`. #### Query Resources to Load the Dropdown Element. We'll load the dropdown element with a list of resources, display the resource name and use the resource ID as the value. Create a backend file called `blockedSessions.web.js`. This file will hold all of our backend code for this tutorial.   First import the `Permissions` enum and `webMethod` function from `wix-web-module`, and then import the `resources` and `sessions` APIs from the `wix-bookings-backend` module. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { resources, sessions } from "wix-bookings-backend"; ``` Create a function called `getResourceList()` that returns an array of all resources. The code will look like this: ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { resources, sessions } from 'wix-bookings-backend'; export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResourceCatalog().find(); return results.items; } catch (error) { return error; } }); ``` Now in your page code, create the `loadResourceDropdown()` function to load the dropdown element. Call the `loadResourceDropdown()` function from the `onReady` code block so the dropdown is loaded when the page is displayed.  Don't forget to import `getResourceList` from the APIs from the `blockedSessions` backend file.  Add the following code to your page to load the  `ddResource` dropdown with resource names and their schedule IDs: * Call `getResourceList()` to get a list of all of your resources.  * Use the  `map` function to build the `resourceSchedules` array from the resource name and scheduleId for each resource in the `resourceCatalog` array. * Set the `options` property of the dropdown element to the array of resource names and schedule IDs. ```javascript import { getResourceList, createSingleBlockedSession } from 'backend/blockedSessions.web' $w.onReady(function () { loadResourceDropdown(); }); export async function loadResourceDropdown() { let resourceCatalog = await getResourceList(); const resourceSchedules = resourceCatalog.map(resource1 => { let resourceEntry = { label: resource1.resource.name, value: resource1.resource.scheduleIds[0] }; return resourceEntry; }); $w('#ddResource').options = resourceSchedules; } ``` Now create an `onClick` function for the button that will gather the information we need, and call the function to create the blocking sessions. ```javascript export async function btnCreateSession_click(event) { let fromDateTime = $w('#inFrom').value; let toDateTime = $w('#inTo').value; let scheduleID = $w('#ddResource').value; try { const session = await createSingleBlockedSession(scheduleID, fromDateTime, toDateTime); console.log("Session", session); } catch (err) { console.error("Session creation failed.", err); } } ``` ### Create A Single Blocked Time Now we can create our blocking session. Create a function in the `blockedSessions.web.js` backend code file called `createSingleBlockedSession()`. ```javascript export const createSingleBlockedSession = webMethod(Permissions.Anyone, async (resourceScheduleId, startDate, endDate) => { try { const sessionInfo = { scheduleId: resourceScheduleId, start: { timestamp: new Date(startDate), }, end: { timestamp: new Date(endDate), }, type: "EVENT", tags: ["Blocked"], }; const options = { suppressAuth: true }; return sessions.createSession(sessionInfo, options); } catch (error) { console.error(error); // Handle the error } }); ``` #### Understanding the code. **Line 4:** Create the `sessionInfo` object using the date from the function's parameters. **Lines 12,13:** Set the session `type` to "`EVENT`" and the `tag` to "`Blocked`". **Line 16:** If you want any user to be able to create a blocked time session, set the `suppressAuth` option to `true`. **Line 17:** Call `createSession()` to create the blocked time session and return the session details to the calling function. This returns to the page code that called the `createSingleBlockedSession()` function, and the created session is displayed in the console. The returned session looks like this: ```json { "_id": "2mmoW0vwKcSFyxtOfCdMeoE8hsC05dc6rdVucf52tUuT6FFcAWCPTKHipQaFL7nOW9qlB9c6Fjm00bZxYfABQpF6kQSpWw6TqFx7", "scheduleId": "278012f1-674e-4a2b-93d2-29f8894ee732", "scheduleOwnerId": "3cdb6d0f-be29-450b-a2e0-184975d95cb3", "start": { "timestamp": "Tue Jun 15 2021 23:00:00 GMT+0300 (Israel Daylight Time)" }, "end": { "timestamp": "Wed Jun 16 2021 00:00:00 GMT+0300 (Israel Daylight Time)" }, "status": "CONFIRMED", "tags": [ "Blocked" ], "type": "EVENT", "notes": "" } ``` ### Recurring Blocked Times Now that we've seen single blocked times working, let's take it up a level and create a series of blocked times using recurrence rules. Recurrence rules specify when and how often an event repeats. A session that repeats every second week on a Monday until January 7, 2022 at 8 AM, is specified using the following recurrence rule:`"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;UNTIL=20220107T080000Z"` #### Page Code When we defined our page, we set the container box to `collapsed`. Expand the box based on the checkbox `cbRecurrence` using the following code: ```javascript export function cbRecurring_click(event) { if ($w('#cbRecurring').checked === true) { $w('#box1').expand(); } else { $w('#box1').collapse(); } } ``` Now that we see the recurrence rule, we can use it to create recurring sessions. Update the button's `onClick`  function with the code below, to add processing for recurring sessions. If the `cbRecurring` checkbox is checked, call  `createRecurringBlockedSession()`, passing the recurrence rule. Otherwise, call `createSingleBlockedSession().` ```javascript export async function btnCreateSession_click(event) { let fromDateTime = $w('#inFrom').value; let toDateTime = $w('#inTo').value; let scheduleID = $w('#ddResource').value; let recurrenceRule = $w('#inRecurrenceRule').value; console.log(scheduleID, fromDateTime, toDateTime); let session = ""; try { if ($w('#cbRecurring').checked) { session = await createRecurringBlockedSession(scheduleID, fromDateTime, toDateTime, recurrenceRule); } else { session = await createSingleBlockedSession(scheduleID, fromDateTime, toDateTime); } console.log("Session", session); } catch (err) { console.error("Session creation failed.", err); } } ``` #### Create A Recurring Blocked Time Session Now we can create our recurring blocking session. Create a function in the `blockedSessions.web.js` backend code file called `createRecurringBlockedSession().` In your page code, add `createRecurringBlockedSession` to your `import` statement. >**Note:** > You must use the `localDateTime` objects for `start` and `end` when defining a recurring session. For single sessions you can use the `timestamp` properties. > Remember that `localDateTime` uses the business's timezone . ```javascript export const createRecurringBlockedSession = webMethod(Permissions.Anyone, async (resourceScheduleId, startDate, endDate, recurrenceRule) => { try { //Create the recurring session object, breaking the start and end dates into their components. const sessionInfo = { scheduleId: resourceScheduleId, start: { localDateTime: { year: startDate.substr(0, 4), monthOfYear: startDate.substr(5, 2), dayOfMonth: startDate.substr(8, 2), hourOfDay: startDate.substr(11, 2), minutesOfHour: startDate.substr(14, 2), }, }, end: { localDateTime: { year: endDate.substr(0, 4), monthOfYear: endDate.substr(5, 2), dayOfMonth: endDate.substr(8, 2), hourOfDay: endDate.substr(11, 2), minutesOfHour: endDate.substr(14, 2), }, }, // Set the session type to "EVENT" and add "Blocked" to the tags array. type: "EVENT", tags: ["Blocked"], recurrence: recurrenceRule, }; //Set the option to suppress permissions checking. const options = { suppressAuth: true }; return sessions.createSession(sessionInfo, options); } catch (error) { console.error(error); // Handle the error } }); ``` #### Understanding The Code **Line 5:** Create the sessionInfo object. You have to break the dates into their components for the `localDateTime` objects. You cannot use the `timestamp` properties for recurring sessions. **Line 26:** Set the session `type` to `"EVENT"`. **LIne 27:** Add `"Blocked"` to the `tags` array. **Line 32:** If you want any user to be able to create a blocked time session, set the `suppressAuth` option to `true`. **Line 33:** Call `createSession()` using the `sessionInfo` and `options` objects, and return the promise to the calling function on the site page. You'll get a session object displayed on the console, similar to the one below. ```json { "_id": "ce6e440d1ef941c08f0e317d75fe5cbc-885c479078d145f992b2bad2a2b5f151", "notes": "", "recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU;UNTIL=20220101T000000Z", "scheduleId": "ce6e440d-1ef9-41c0-8f0e-317d75fe5cbc", "scheduleOwnerId": "3df00036-ac5a-4f8b-bf0d-4f77b1795b1d", "start": { "localDateTime": { "year": 2021, "monthOfYear": 7, "dayOfMonth": 1, "hourOfDay": 9, "minutesOfHour": 0 } }, "end": { "localDateTime": { "year": 2021, "monthOfYear": 7, "dayOfMonth": 1, "hourOfDay": 10, "minutesOfHour": 0 } }, "status": "UNDEFINED", "tags": [ "Blocked" ], "type": "EVENT" } ``` ### Check that it Worked. In addition to the returned session object, you can also check your [calendar](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fbookings/scheduler/owner/calendar) to confirm that the blocked time appears for your resource at the time that you specified.
### All the Code Below is all of the code used in the tutorial in one place. #### Page Code ```javascript import { getResourceList, createSingleBlockedSession ,createRecurringBlockedSession} from 'backend/blockedSessions.web' $w.onReady(function () { loadResourceDropdown(); }); export async function loadResourceDropdown() { let resourceCatalog = await getResourceList() const resourceSchedules = resourceCatalog.map(resource1 => { let resourceEntry = { label: resource1.resource.name, value: resource1.resource.scheduleIds[0] } return resourceEntry }) $w('#ddResource').options = resourceSchedules; } export async function btnCreateSession_click(event) { let fromDateTime = $w('#inFrom').value; let toDateTime = $w('#inTo').value; let scheduleID = $w('#ddResource').value; let recurrenceRule = $w('#inRecurrenceRule').value; console.log(scheduleID, fromDateTime, toDateTime); let session = ""; try { if ($w('#cbRecurring').checked) { session = await createRecurringBlockedSession(scheduleID, fromDateTime, toDateTime, recurrenceRule); } else { session = await createSingleBlockedSession(scheduleID, fromDateTime, toDateTime); } console.log("Session", session); } catch (err) { console.error("Session creation failed.", err); } } export function cbRecurring_click(event) { if ($w('#cbRecurring').checked === true) { $w('#box1').expand() } else { $w('#box1').collapse() } } ``` #### blockedSessions.web.js ```javascript import { Permissions, webMethod } from "wix-web-module"; import { resources, sessions } from "wix-bookings-backend"; export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResourceCatalog().find(); return results.items; } catch (error) { throw error; } }); export const createSingleBlockedSession = webMethod(Permissions.Anyone, async (resourceScheduleId, startDate, endDate) => { const sessionInfo = { scheduleId: resourceScheduleId, start: { timestamp: new Date(startDate), }, end: { timestamp: new Date(endDate), }, type: "EVENT", tags: ["Blocked"], }; const options = { suppressAuth: true }; try { return await sessions.createSession(sessionInfo, options); } catch (error) { throw error; } }); export const createRecurringBlockedSession = webMethod(Permissions.Anyone, async (resourceScheduleId, startDate, endDate, recurrenceRule) => { //Create the recurring session object, breaking the start and end dates into their components. const sessionInfo = { scheduleId: resourceScheduleId, start: { localDateTime: { year: startDate.substr(0, 4), monthOfYear: startDate.substr(5, 2), dayOfMonth: startDate.substr(8, 2), hourOfDay: startDate.substr(11, 2), minutesOfHour: startDate.substr(14, 2), }, }, end: { localDateTime: { year: endDate.substr(0, 4), monthOfYear: endDate.substr(5, 2), dayOfMonth: endDate.substr(8, 2), hourOfDay: endDate.substr(11, 2), minutesOfHour: endDate.substr(14, 2), }, }, // Set the session type to "EVENT" and add "Blocked" to the tags array. type: "EVENT", tags: ["Blocked"], recurrence: recurrenceRule, }; //Set the option to suppress permissions checking. const options = { suppressAuth: true }; try { return await sessions.createSession(sessionInfo, options); } catch (error) { throw error; } }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating a Custom Bookings Experience ## Article: Creating a Custom Bookings Experience ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/creating-a-custom-bookings-experience.md ## Article Content: # Velo Tutorial: Creating a Custom Bookings Experience Using the Velo Bookings API you can create a custom booking experience for the services you offer on your site. ### Prerequisites Before working with the Bookings API, [set up your services](https://support.wix.com/en/wix-bookings/setting-up-wix-bookings) using the Bookings App. If you will be taking payments for bookings, you need to set up your site to accept payments before using the Bookings API. To learn more, see [About Accepting Payments](https://support.wix.com/en/article/about-accepting-payments). To check out bookings with the Bookings API you need to [upgrade to a Business Premium Plan](https://support.wix.com/en/article/upgrading-wix-bookings).
**Note:** When setting up your site to accept payments, be sure to select the payment methods you want to offer and [set your payment currency](https://support.wix.com/en/article/setting-your-currency-for-accepting-payments).
### Bookings API The Bookings API consists of two client-side functions used to get a service's available slots and to book one of the available slots: * `getServiceAvailability()` - Called to retrieve available slots for a given service. Before calling this function you must first retrieve the ID of the service to be booked. * `checkoutBooking()` - Called to book a service and to prompt the current site visitor to enter payment information if required. This function requires you to pass the specific service slot to be booked and values for any form fields that are needed when booking the service. For detailed information on the Bookings API see [wix-bookings-frontend](https://dev.wix.com/docs/velo/api-reference/wix-bookings-frontend/introduction.md) in the API Reference. ### Bookings Lifecycle The following list outlines the steps taken in a standard booking lifecycle to demonstrate how the Bookings API can be used. Remember, you do not need to follow this flow. You can use the Bookings API to create a custom booking flow that meets your site's specific needs. 1. You get a list of services from the **Bookings/Services** collection and display them. 2. A user selects one of the displayed services. 3. You call the `getServiceAvailability()` function using the selected service's **Service ID** (`_id`) value. You get that ID from the results of the query performed above. (Optionally, you can pass a `ServiceAvailabilityOptions` object to change the limits on the slots that are returned.) 4. You display the returned slots. You may also want to retrieve the staff member items that relate to the returned slots from the **Bookings/Staff** collection. 5. You also display input elements to gather the service's form fields if there are any. You retrieve the list of form fields from results of the query performed above in the `fields` property. 6. A user enters values for the form fields and indicates that the booking should be checked out. 7. You call the `checkoutBooking()` function. You pass the selected slot object, the values for the form fields, and the payment type if necessary. Note, the specified payment type must match the service's configuration in your site's **Dashboard**. You cannot book a paid service as if it were free. * If the service is free, you do not need to pass a `paymentOptions` object. * If the service is not free and you pass a `paymentOptions` object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information. * If the service is not free and you pass a `paymentOptions` object indicating the payment should be offline, the payment popup is not presented to the user. ### Bookings Example The following is an example that shows how to use page elements and code to achieve the sample bookings lifecycle described above. The example steps correspond to the numbers in the list above.
**Note:** In this example, we focus on the code that drives the bookings lifecycle. We do not include any additional code that helps guide the user through the booking process. You might want to split the lifecycle into sections on your page and expand or collapse the sections depending on what is relevant for the user at a given point  in the lifecycle.
#### Step 1: Retrieve and Display Services You can retrieve your site's list of services and display them in a number of ways. In this example, we use a dataset connected to a repeater. This allows us to fully customize what service data is displayed and how we display it using the least amount of code. Another option for retrieving the service list is: * Query your site's **Bookings/Services** collection.  Other options for displaying the service list are: * Table * Gallery * Dropdown * Radio Buttons * Checkboxes In our example, we add a dataset with the ID **bookingsDataset** connected to the **Bookings/Services** collection. The following page elements are connected to fields in the collection through the dataset: |Type |ID |Connected to field | |---|---|---| |Repeater|servicesRepeater|\- |Text|titleText|Service Name |Image|serviceImage|Service Image |Text|taglineText|Service Tagline |Text|priceText|Price Summary |Button|bookButton|\-
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/11/20/9a8edf94-1715-4dae-9a00-9f5e05b55d28.png)
#### Step 2: User Selects Service Depending on how you choose to display your services, you will need to react to a user's selection of a service differently. The bottom line is that you have to get the ID of the service that the user selected. In our example, the user clicks a button in a repeater connected to dataset, so we can use the button's click event to get the ID of the selected service. We add an **onClick** event handler to the **bookButton** using the Properties & Events Panel.  ```javascript export function bookButton_click(event) { } ``` #### Step 3: Retrieve Available Slots You retrieve the selected service's available slots by calling `getServiceAvailability()`. The function requires that we pass it the ID of the selected service. In our example, we've already gotten the ID of the selected service. So all we need to do is use it when calling `getServiceAvailability()`. First, we need to import the Bookings API all the way at the top of our page's code. ```javascript import wixBookingsFrontend from 'wix-bookings-frontend'; ``` Then, we can add the function call to the event handler we created in the previous step. We need to store the slot information for later, so we also create the following variables: * availableSlots * slotOptions * selectedSlot * slotIndex ```javascript let availableSlots; let slotOptions; let selectedSlot; let slotIndex; export function bookButton_click(event) { wixBookingsFrontend.getServiceAvailability(event.context.itemId) .then( (availability) => { availableSlots = availability.slots; } ); } ``` When calling `getServiceAvailability()`, you can optionally pass an object that defines a datetime range that refines which slots will be returned. #### Step 4: Display Available Slots Once again, you can display the data we just retrieved in a number of ways. In this example, we use a table to display all the available slots. We have to do a little processing of the slot data to get it into the right format for the table.  Our table's ID is **slotTable**, it has only 1 column whose **Field Name** is **slotDate**. Again, we add some code to the event handler created above. We store the slot options in the slotOptions array, although we only display the **slotDate** property in the table. We'll use the **ID** property later in Step 8.  ```javascript export function bookButton_click(event) { wixBookingsFrontend.getServiceAvailability(event.context.itemId) .then( (availability) => { availableSlots = availability.slots; //---Added in this step---// slotOptions = availableSlots.map((slot) => { let date = slot.startDateTime; return { "slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(), } }); $w('#slotTable').rows = slotOptions; //---End of added in this step---// } ); } ``` #### Step 5: Store the User's Selected Slot We'll need to know which slot the user selected in the table. To do that we need to go to the table setting and set **Clicking selects** to **Rows**. Then we'll use the Properties & Events Panel to add an **onRowSelect** event to the table.  When the user clicks on a row we want to store the slot information for their selected row, so we'll add code to the event handler: ```javascript export function slotTable_rowSelect(event) selectedSlot = event.rowData; slotIndex = event.rowIndex; } ``` #### Step 6: Display Form Fields We also need to display input elements to gather the selected service's form fields. Here we use a repeater with the ID **formFieldRepeater**. Each item in the repeater consists of a single text input with the ID **fieldInput**. Each item in the repeater will be used to collect one of the service's form fields. Here, we also choose to only collect the required information. First, we add some code to the event handler created above. ```javascript export function bookButton_click(event) { wixBookingsFrontend.getServiceAvailability(event.context.itemId) .then((availability) => { availableSlots = availability.slots; slotOptions = availableSlots.map((slot) => { let date = slot.startDateTime; return { "slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(), } }); $w('#slotTable').rows = slotOptions; }); //---Added in this step---// let $item = $w.at(event.context); let formFields = $item("#bookingsDataset").getCurrentItem().form.fields; formFields = formFields.filter(field => field.constraints.required) $w('#formFieldRepeater').data = formFields; //---End of added in this step---// } ); } ``` Then, we add an event handler for the repeater that sets the placeholder text in each of the text input elements. ```javascript export function formFieldRepeater_itemReady($item, itemData, index) { $item("#fieldInput").placeholder = itemData.label; } ``` The repeater looks like this in the editor (there is an input element inside the repeater):
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/11/20/a1df6093-09b2-46ed-9e99-23dc1df56209.png)
And it looks like this on the site when populated:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/11/20/93a53bed-4f3c-4391-b427-2966aafa8ddd.png)
#### Step 7: User Enters Form Field Values and Books Service At this point, the user enters values for the form fields in the repeater we just set up. Then the user indicates that we should process the booking. In our example, we use a button with the ID **checkoutButton** for this purpose. We add an **onClick** event handler to the **checkoutButton** using the Settings Panel. Here we'll collect all of the data that the user entered into the form fields. ```javascript export function checkoutButton_click(event) { let formFieldValues = []; // list of values user entered // for each item in the repeater $w('#formFieldRepeater').forEachItem( ($item, itemData, index) => { // add an object containing the corresponding form field's // ID and the value that the user entered formFieldValues.push( { "_id": itemData._id, "value": $item("#fieldInput").value } ); } ); } ``` #### Step 8: Booking Checkout You perform a booking checkout by calling `checkoutBooking()`. The function requires that we pass it the selected slot object and the form field values that the user entered. We package these together in an object named `bookingInfo`. Remember, we've already collected the form field data in a variable named `formFieldValues`. When calling `checkoutBooking()`, you can optionally pass an object that defines the payment options. In our example, we are using services which are paid for offline, so we will not be passing any payment options. We also handle the result returned by the checkout. We display the checkout status to the user in a text element with the ID **confirmationText**. ```javascript export function checkoutButton_click(event) { let formFieldValues = []; $w('#formFieldRepeater').forEachItem( ($item, itemData, index) => { formFieldValues.push( { "_id": itemData._id, "value": $item("#fieldInput").value } ); } ); //---Added in this step---// // build bookingInfo object let bookingInfo = { // selected slot object "slot": availableSlots[slotIndex], // form filed values collected above "formFields": formFieldValues }; // booking checkout wixBookingsFrontend.checkoutBooking(bookingInfo) .then( (results) => { $w("#confirmationText").text = `Booking ID: ${results.bookingId} Status: ${results.status}`; } ); //---End of added in this step---// } ``` When a checkout is performed, the user is presented with an experience that reflects the service's payment type. * If the service is free, the checkout is booked without any other input. * If the service is not free and you pass a `paymentOptions` object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information. * If the service is not free and you pass a `paymentOptions` object indicating the payment should be offline, the payment popup is not presented to the user. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Bookings Pricing Service Plugin ## Article: Bookings Pricing Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-bookings/tutorial-bookings-pricing-service-plugin.md ## Article Content: # Velo Tutorial: Bookings Pricing Service Plugin Wix service plugins allow you to expand what your site can do by integrating with 3rd-party services not currently supported by Wix. They also allow you to implement custom logic to change how your site displays and behaves. For example, when you set up Wix Bookings, there is only a single price per service. What if you want to offer different service prices to your customers, such as varied pricing based on different choices? Or different rates for weekends and holidays? Service plugins are the answer.  You can manage multiple service plugins from the **Public & Backend Code** section in the [Code sidebar](https://docs.google.com/spreadsheets/d/1tCBuqrexV3XCAKCozkUufXO5qFeXmf9hX2DZ3UJC07U/edit?usp=sharing). Learn more about [service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/about-service-plugins.md).  With a Bookings Custom Pricing plugin, you can implement custom pricing options using code. You can also connect your site to external pricing rate providers whose functionality is not currently supported by Wix. These prices can be displayed on your site's checkout pages.  This tutorial explains how to set up a custom pricing plugin on your site using Velo. The process has 3 steps: 1. Create a Bookings Pricing plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. ## Step 1: Create a Bookings Pricing plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar that contains the files for your backend code. 1. [Add Wix Bookings](https://www.wix.com/app-market/web-solution/bookings) to your site. 2. With Velo Dev Mode enabled, click the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/1c4d0337-43f1-4f62-a125-758ae18c0228/618cf4f4-ac25-4ab2-8998-d3f511c91d30.png)  tab on the Code sidebar. 3. Scroll down to the **Service Plugins** panel at the bottom of the sidebar.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3b28ebcfbf8f2b6165f835d528f2f55c.png)
4. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png) . 5. Select **Bookings Pricing**.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ddfb53fdbbc7d7293d05109b3f5f7cec.png)
6. Enter a name for your plugin and click **Add & Edit Code**. The name can't contain spaces or special characters. Hyphens are allowed.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/66410b374e87edca734a3aa7445fc02e.png)
## Step 2: Implement the plugin The procedure in the previous section creates a folder called **bookings-custom-pricing** under **Service Plugins** in the **Public & Backend Code** section of the Code sidebar. Inside this folder is another folder with the name of the plugin you set up. This folder contains 2 default plugin files: * `.js`: The code in this file generally defines a function named after the purpose of the service plugin, such a `calculatePrice()`. The function is called by Wix to retrieve the data provided by your plugin. * `-config.js`: The code in this file generally defines the name of the main service plugin function. Implement the custom code for your plugin in these files. Here are some guidelines for writing your code: ### .js The code in this file defines a function named `calculatePrice()`. Wix Bookings calls this function at checkout to retrieve the custom pricing options provided by your plugin. The function accepts the following parameters: * **options:** An object holding information about the booking, including the service, location, time, and participants. For more details, see the [service plugin reference](https://dev.wix.com/docs/velo/events-service-plugins/bookings/service-plugins-spis/bookings-custom-pricing/introduction.md). Sample **options** object: ```json { "options": { "booking": { "numberOfParticipants": 2, "endDate": "2023-04-11T19:00:00.000Z", "bookingSource": { "platform": "WEB", "actor": "CUSTOMER" }, "sendSmsReminder": false, "flowControlSettings": { "withRefund": false }, "revision": "1", "_id": "35f6fd1f-df83-4195-bd67-7f7237b428b5", "createdBy": { "anonymousVisitorId": "869f0e9d-40be-4f2b-a415-14f1316f295e" }, "selectedPaymentOption": "ONLINE", "contactDetails": { "firstName": "Joy Smith", "email": "joysmith@company.com", "phone": "0001231234" }, "additionalFields": [ { "_id": "709498bf-e275-4c82-aa9c-e76d3d4e9af6", "value": "1", "label": "Addon 1" }, { "_id": "35a296f6-d71e-4d61-b8ef-cd2973cd9e99", "value": "1", "label": "Addon 2" }, { "_id": "e3fe7974-3142-4fd5-993c-41472f36f697", "value": "0", "label": "Addon 3" }, { "_id": "9f1252be-0625-457b-b5cf-96a1fa42281a", "value": "false", "label": "weekendRate", "valueType": "CHECK_BOX" } ], "bookedEntity": { "slot": { "location": { "_id": "f8290483-b327-418e-8aab-0a661c80a46f", "name": "5th Avenue", "formattedAddress": "1002 5th Avenue, New York, NY, USA", "locationType": "OWNER_BUSINESS" }, "endDate": "2023-04-11T15:00:00.000-04:00", "timezone": "America/New_York", "resource": { "_id": "76570209-101f-409b-af97-b445bdb63125", "name": "Bathhouse Staff", "scheduleId": "7046edd2-0ee0-43d1-a60f-2ed9cfd36b64" }, "scheduleId": "c4f2bde3-c327-4424-a99f-7fe0294d419c", "sessionId": "4jOkD28c0FrsNUSgzQzuLRkA2t1rv1FLTwquKmJyctoZm00vdeKFMyH4n9cCLtmQe8wwAEJlPoJAU9LSPJu26G42joedYythPDtgGeAtC7N5ThJYEmZmm8qrccxj7YxJ36UA6sb9deXOfkwnK2r9A5KWYeM77OPTjaIupKlbIU186GE5wQEBmM2uHCyMUqOvOtoDPyGMJXTlr3DAVEYYiIL0N3VXBmHbBVD25LmgexOYDN2MqqZ3eLMX5wVArHPeJPAWeI5bfVKgSi0ozO16W66hj6HudqeIem8fuHQdEqDXgfkVsUE8QPagmlsf8abmjGqFtSPEI7jsoOb", "startDate": "2023-04-11T12:00:00.000-04:00", "serviceId": "851bca8b-dd5b-4575-8e66-f0bcaca2bfdf" }, "title": "Bathhouse session", "tags": [ "GROUP" ] }, "startDate": "2023-04-11T16:00:00.000Z", "participantNotification": { "notifyParticipants": true }, "_updatedDate": "2023-04-09T10:48:19.751Z", "totalParticipants": 2, "_createdDate": "2023-04-09T10:48:19.751Z" } } } ``` * **context:** An object that holds the values or variables that are available and relevant within the scope of the function during its execution. The `calculatePrice()` function must return an object with a `calculatedPrice` integer. This objects defines the booking's price that site visitors see on the **Checkout** page. For more details, see the [service plugin reference](https://dev.wix.com/docs/velo/events-service-plugins/bookings/service-plugins-spis/bookings-custom-pricing/introduction.md). Example return value: ```json { "calculatedPrice": 16.0 } ``` ### -config.js The code in this file defines a function named `getConfig()` that returns the name of the main service plugin function that is defined in the `.js` file as an object. This is important because you can have multiple `.js` files and multiple functions in these `.js` files. In our case, the main service plugin function name is `calculatePrice()`. Example `getConfig()` function: ```js export function getConfig() { return {pricingProviderName: "calculatePrice"} } ``` Example return object:  ```json { "pricingProviderName": "calculatePrice" } ``` ### Add files to the plugin If you don't want to keep all of your code in the main plugin files, you can add files to the plugin's folder and import functions and objects into the main files. 1. Hover over the plugin folder's name and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/d7a7ee13-5c33-4de8-bba0-d8a883585472/f793ba37-bd42-4377-bd93-b3936e7215c5.png) . 2. Select **New .js file**. 3. To import from these files to the main plugin files, use the following syntax: ```javascript import { functionName } from './myFileName.js'; ``` ### Test the plugin You can test your plugin before publishing your site using [functional testing](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/functional-testing/test-backend-functions-with-functional-testing.md) like you would with any backend Velo code. Make sure your `calculatePrice()` function's return values are properly formatted. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 3: Deploy the plugin Once your code files are ready, you need to publish the site. 1. Publish your site. > **Note** > There may be a delay between publishing the site and the new bookings pricing options appearing on the live site. ## Update the plugin You can make updates to your plugin as necessary. Make sure to publish your site for your updates to take effect. ## Remove the plugin You can remove the plugin from your site from the Code sidebar. 1. Hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/d7a7ee13-5c33-4de8-bba0-d8a883585472/f793ba37-bd42-4377-bd93-b3936e7215c5.png) . 2. Select **Remove.
3. Click **Remove**. ## Example We created an example to demonstrate how you can use service plugins to extend the pricing for bookings on your site. ### Offer custom pricing for add-ons and weekend rates In this example, we calculate and display custom pricing that offers lower rates for weekdays and varied pricing for different addon options. To test this code, paste it into your **.js** file. Custom plugins allow you to implement pricing with your own customized business logic and display the new pricing on your site during checkout. In this example, we calculate and display custom pricing that offers lower rates for weekdays and varied pricing for different addon options. We use the `addtionalFields` property of the booking to get the weekend rate and the price of each addon. To test this code, paste it into your `.js` file. ```javascript import { getPrices } from 'backend/queries'; export const calculatePrice = async (options, context) => { const prices = getPrices(); const additionalFields = options.booking.additionalFields; const numberOfParticipants = options.booking.numberOfParticipants; const weekendRate = getFieldValue(additionalFields, "weekendRate"); const addon1Value = getFieldValue(additionalFields, "Addon 1"); const addon2Value = getFieldValue(additionalFields, "Addon 2"); const addon3Value = getFieldValue(additionalFields, "Addon 3"); const rate = weekendRate === "true" ? prices.weekend : prices.weekday; const finalPrice === numberOfParticipants * rate + addon1Value * prices.addon1 + addon2Value * prices.addon2 + addon3Value * prices.addon3; return {calculatedPrice: finalPrice}; }; export function getFieldValue(additionalFields, text) { const foundFieldArray = additionalFields.filter(additionalField => additionalField.label === text); return foundFieldArray.length === 0 ? undefined : foundFieldArray[0].value; } ``` In this example, the booking's price is modified based on add-ons the customer can add to their booking, and a more expensive rate if the booking is for the weekend. We built a [sample site](https://www.wix.com/velo-examples/custom-service-price/bookflow) where you can see this code in action. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating a Custom Registration Form with Code ## Article: Creating a Custom Registration Form with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/creating-a-custom-registration-form-with-code.md ## Article Content: # Velo Tutorial: Creating a Custom Registration Form with Code When you add membership functionality to your site, you receive a default form for registering site visitors as site members. Sometimes the standard form doesn't meet the specific needs of your site. In this article, we demonstrate how to build a custom registration form that you can design to match your site's branding, and use it to collect specific information from your site visitors in the registration process. Here we create an example custom registration form for a store called The Tall Shop. To create a custom registration form we: 1. Enable custom site registration using Velo Forms. 2. Build a custom Velo Form with input and design elements. 3. Set up the custom fields and labels in our site's Contact List. 4. Write page code to perform membership registration using the information captured from our custom form. ### Prerequisites Make sure you’ve [added a Members Area to your site](https://support.wix.com/en/article/adding-a-members-area-to-your-site). This article assumes you are familiar with the following: * [Creating an input form](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/processing-user-input-before-it-is-stored-in-a-collection-with-data-hooks.md) * [Wix Members Area](https://support.wix.com/en/article/about-the-members-area) * [Popups](https://support.wix.com/en/article/wix-editor-adding-and-setting-up-a-lightbox) * [Your site's Contact List](https://support.wix.com/en/article/about-your-contact-list) ### Step 1 | Enable Custom Site Registration To get started, follow the steps below to enable custom site registration using Velo forms. 1. [Add a popup to your site](https://support.wix.com/en/article/studio-editor-using-popups). In our example, we name our popup **Custom Registration Form**. 2. Navigate to your **Pages** on the left side of your editor. Under **Signup & Login**, click **Member Signup Form**.  
3. In the dropdown menu, change the default form to **Velo Form**, and link it to the **Custom Registration Form** popup we created earlier. This step changes the default member signup form to the Velo custom registration form. Also, disable **Only allow backend calls for Signup and Login APIs**.
We can now go to our popup page to start building our form. ### Step 2 | Build a Custom Form Next, we set up our site's contact list so that we can specify additional information to the contact entry created when a member registers using our custom form. You can skip this step if you don't need any additional data from your site members. In our example, we set up some labels and a custom field. > **Notes:** > * When a site visitor signs up using a Velo custom registration form, they automatically become a site member, as the member Signup Setting defaults to automatic approval for everyone who signs up. If you want to manually approve them before becoming a member, set your member Signup Setting to **Only people who I approve manually**.  > * Choosing manual approval means that site visitors who register will have the status of ‘Pending’ until you manually approve them. You’ll have to go to your site's Contact List to approve them. To learn more, see the [Automatic vs. Manual Approval](https://www.wix.com/velo/reference/wix-members/authentication/register#wix-members_authentication_register_automatic-vs-manual-approval) section of the register() API. > * If you choose manual approval, you may want to use the [register() API from the wix-members-backend module](https://www.wix.com/velo/reference/wix-members-backend/authentication/register) instead. This allows you to build more secure approval flows by keeping tokens hidden from the frontend. Your site’s Contact List has multiple fields built in, such as ‘Name’, ‘Email’, and ‘Phone’, shown below. If you want to collect this data from your site visitors, simply add input elements to your site for these fields. The data collected from the input elements is stored in the corresponding fields in your site’s Contact List.
**Important** Your form must include at least 2 input elements to collect your site visitor’s email and password. This data is required for adding a new contact entry for your site visitor in your site’s Contact List, and for registering them as a site member.  If you want to collect additional data from your site visitors aside from the default fields in your site’s Contact List, you’ll need to add custom fields to your site’s Contact List. In our example, we create a custom field to store our site visitor’s height, and we add an input element to our form to collect this information.
To build our custom registration form, we add input elements to our popup page. We include inputs for required fields (email and password), for existing fields in your site’s Contact List (first name and last name), and we add an additional input element for collecting our site visitor’s height. Lastly, we add a register button to submit the form and register the site visitor as a new member.  Add the following elements to your popup page: * First Name  * Last Name   * Email address   * Password   * Height  * Register button Your custom registration form should look something like this:
|Type |ID |Usage | |---|---|---| |Input - Text|firstName|For entering a first name |Input - Text|lastName|For entering a last name |Input - Email|email|For entering an email address |Input - Password|password|For entering a password |Input - Number|height|For entering a height |Button|register|For triggering the registration code ### Step 3 | Create a Custom Field [Custom fields](https://support.wix.com/en/article/adding-custom-fields-to-contacts) are used to store additional information about your site's contacts.  In the previous step, we added an additional input element to our form to collect our site visitor’s height. Now we add a corresponding custom field in our site’s Contact List to store our contact’s height. In your site’s Dashboard, go to your contact list and click **More Actions > Manage custom fields** and add a new field named **height**. Set the **height** field's type to **Number**. Note that you can add any custom fields that suit the needs of your site.
### Step 4 | Add Labels [Labels](https://support.wix.com/en/article/creating-contact-labels) are used to organize your contacts into meaningful groups. In our example, we use labels to organize our members into different height groups. In our code, we calculate a new member's label based on the height they provide in our registration form. In your site’s Dashboard, go to your site’s Contact List and click **More Actions > Manage labels**. Add the following labels in your site’s Contact List: * Not Tall * Tall * Very Tall * Too Tall
### Step 5 | Add Code The last step is writing the code for our popup page. Let's first take a look at this code piece by piece to understand what it's doing. #### Import the API and Declare Variables ```javascript import { authentication } from 'wix-members-frontend'; let emails = []; let labels = []; ``` **Line 1**: First, we import the authentication API from the wix-members-frontend module. **Lines 3- 4**: We declare 2 arrays to store the new member's email address and label. Note that in our example, new members will have only one email address and one label. However, the API requires that both of these are passed as arrays. #### Set up the Form Event Handler to Retrieve Input Values ```javascript $w.onReady(function () { $w('#register').onClick( () => { const password = $w('#password').value; const height = Number($w('#height').value); const email = $w('#email').value; emails.push(email); ``` **Lines 1- 2**: In the onReady() function, we add an onClick() event handler to the register button. When a site visitor finishes entering their information in our form and clicks **Register**, the data they provided for each input element is used to automatically add a new contact to our site's contact list.  **Line 3- 5**: In the event handler, we set the password, height, and email variables to the corresponding input values. **Line 6**: We take the email address the site visitor provided in the form, and store it in the emails array. #### Add a Height Label to the Contact ```javascript // Calculate the proper height label if(height < 74){ labels.push('Not Tall'); } else if (height < 78) { labels.push('Tall'); } else if (height < 82) { labels.push('Very Tall'); } else { labels.push('Too Tall'); } ``` To learn more about the register function, see the [register() function](https://www.wix.com/velo/reference/wix-members/authentication/register) in the API Reference. **Lines 1- 10**: We calculate which label (‘Not Tall’, ‘Tall’, ‘Very Tall’, or ‘Too Tall’), to add to the new contact based on the height value provided in the form. This height label is added to the contact’s entry along with the other data provided in the form.  #### Register Site Visitor with the Details Provided in the Form ```javascript let options = { contactInfo: { firstName: $w('#firstName').value, lastName: $w('#lastName').value, emails: emails, labels: labels, height: Number($w('#height').value) }, privacyStatus: 'PUBLIC' } authentication.register(email, password, options) .then((registrationResult) => { const status = registrationResult.status; // When the site is configured for automatic approval, status is "ACTIVE" and the member is approved and logged in. console.log('Member registered and logged in:', registrationResult); }) .catch((error) => { console.error(error); }); }); }); ``` **Lines 1- 10**: Before calling the register() function, we first set the options we want to pass in. The options object includes a contactInfo object with the fields and labels in our site’s Contact List, and their corresponding data values collected from our form. We also set the new member’s [privacy status](https://support.wix.com/en/article/wix-groups-member-privacy-settings-for-groups) to 'PUBLIC' upon registering. Note that our contactInfo object reflects the information we collect in our form. It also reflects the specific labels and custom fields that we created in our site’s Contacts List. **Line 12**: We call the authentication API’s register() function and pass the email and password collected in the form, as well as the registration options we set above.  **Lines 13- 22**: We use the .then() function to retrieve the  registrationResult object returned by the register() function. If this object’s status property is ‘ACTIVE’, the member is approved and logged in. Last, we log the registrationResult object to the console.  Here is the complete code for this example: ```javascript import { authentication } from 'wix-members-frontend'; let emails = []; let labels = []; $w.onReady(function () { $w('#register').onClick( () => { const password = $w('#password').value; const height = Number($w('#height').value); const email = $w('#email').value; emails.push(email); // Calculate the proper height label if(height < 74){ labels.push('Not Tall'); } else if (height < 78) { labels.push('Tall'); } else if (height < 82) { labels.push('Very Tall'); } else { labels.push('Too Tall'); } let options = { contactInfo: { firstName: $w('#firstName').value, lastName: $w('#lastName').value, emails: emails, labels: labels, height: Number($w('#height').value) }, privacyStatus: 'PUBLIC' } authentication.register(email, password, options) .then((registrationResult) => { const status = registrationResult.status; // When the site is configured for automatic approval, status is "ACTIVE" and the member is approved and logged in. console.log('Member registered and logged in:', registrationResult); }) .catch((error) => { console.error(error); }); }); }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Building Your Own Members Area ## Article: Building Your Own Members Area ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/building-your-own-members-area.md ## Article Content: # Velo Tutorial: Building Your Own Members Area
Visit the Velo by Wix website to onboard and continue learning.
>**Note:** > You can now [add a members area to your site](https://support.wix.com/en/article/adding-a-members-area-to-your-site) without using code. You can also [create a page that displays items based on the currently logged-in member](https://support.wix.com/en/article/creating-custom-member-profile-pages) without using code. In this article we demonstrate how to create a profile page for each of your site members. The data for the dynamic profile page is created when a site visitor first logs in to your site. Members can view and update their personal profiles at any time.   >**Note:** > The functionality for logging members in and out of your site only works fully when viewing your published site. You will not be able to fully test your member profile pages in preview mode. To create our member profile pages we build: * A members collection * A profile page * An update page * A login page This article assumes you are familiar with the following concepts: * [Database collections](https://support.wix.com/en/article/about-database-collections) * [Collection permissions](https://support.wix.com/en/article/about-database-collection-permissions) * [Page permissions](https://support.wix.com/en/article/applying-page-permissions) * [Dynamic item pages](https://support.wix.com/en/article/setting-up-a-dynamic-list-page) * [Dataset Modes](https://support.wix.com/en/article/working-with-dynamic-page-dataset-settings) * [Data Binding](https://support.wix.com/en/article/about-data-binding-and-datasets) ### Members Collection We begin by creating a collection named **Members**, which is where we store all of the member data. Each item in the collection is another member. Because we want to restrict access to members so they can only view and update their own items, we set the permissions for the **Members** collection to **Custom Use** and choose the following: * Who can read content from this collection? - **Site member author** * Who can create content for this collection? - **Site member** * Who can update content from this collection? - **Site member author** * Who can delete content from this collection? - **Admin** The collection can contain whatever member data you want. In this example, we use the following fields: |Field Name |Field ID |Type | |---|---|---| |Title|title|Text |First Name|firstName|Text |Last Name|lastName|Text |Email|email|Text |Phone|phone|Text |About|about|Text ### Profile Page Next, we create a page to display member profiles. It is a dynamic item page connected to the **Members** collection. Because we are creating a dynamic item page, we need to choose a field to add to the URL that uniquely identifies each member. Here we use each member's automatically generated IDs to uniquely identify them.  The URL looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/91f39f0c-8c58-4db6-a1e9-4c658c9723c1/2017/12/04/72d0bc8d-b3ee-4b9f-a8eb-717a27785b36.jpg)
Because we're only using this page to display data, we set the dynamic dataset's mode to **Read-only**. We also set the page's permissions to **Members Only** using the **Permissions** tab of the **Page Settings** so that only members who are logged in can reach the page. The page itself is designed with elements for displaying the information in a member's profile. In our example, that means text elements that are connected through the page's dynamic dataset to the fields in the **Members** collection. Finally, we add a button element. When we create the update page, we will connect the link of the button to navigate to the update page for the current member. When members check their profile pages, they will see something like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/26/67dae2e0-381c-4e50-b149-61d95b767a2a.png)
### Update Page The update page is very similar to the profile page. It is also a dynamic item page connected to the **Members** collection. It also uses the members' IDs as the unique identifying field. However, because we can't have two pages with the same URL in our site, we add **Update** to the URL so it looks like this: 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/91f39f0c-8c58-4db6-a1e9-4c658c9723c1/2017/12/04/4d4c8e20-c73e-4a39-8088-802bd42c5c76.jpg)
Because we're displaying data on this page and allowing members to edit their data, we set the dynamic dataset's mode to **Read & Write**. Once again we set the page's permissions to **Members Only** using the **Permissions** tab of the **Page Settings** so that only members who are logged in can reach the page. This time the page is designed with elements for displaying and editing the information in a member's profile. In our example, we'll use a dropdown element, some text input elements, and a text box element. All these elements are connected through the page's dynamic dataset to the fields in the **Members** collection. Finally, we add a button element for submitting edited data. We connect the submit button using the **Connect to CMS** button as follows: | Field | Value | | ------------------------ | ---------------- | | Choose a dataset | Members Item | | Click action connects to | Profile Page | When members see their update pages, they will see something like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/26/49208fe8-9e4e-4009-a15e-07599033f187.png)
Now that we've created the update page, we can return to the profile page to link the update button. We connect the update button using the **Connect to CMS** button as follows: | Field | Value | | ------------------------ | ---------------- | | Choose a dataset | Members Item | | Click action connects to | Update Page | ### Login Page In our example we have a dedicated page for members to use for logging into the site. However, you can set this page as your homepage, add the contents of this page to any page, or [show them on all pages](https://support.wix.com/en/article/showing-an-element-on-all-pages-6533570).  >**Note:** > Because we have a dedicated login page, we'll be adding the code below to this page. However, if you're showing the elements on all pages, you need to add the code below to **masterPage.js**. The design of our login page is very simple. It contains two buttons. The first button is used for either logging in or logging out, depending on whether the current member is already logged in or not. The second button is used to navigate to a member's profile page. It is only shown for members who are already logged in.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/09/04/4f007dec-b68d-4c07-9a88-02b300b2cb3a.png)
#### Code The code on our login page consists of four parts: * Imports for the APIs used in the rest of the code * An onReady event handler for setting up the page when it loads * A login button onClick event handler for either: * logging in a member and possibly creating a new item in the **Members** collection if it is a first-time login * logging out a member who is already logged in * A profile button onClick event handler for navigating members to their profile pages * * * Let's take a look at the code piece by piece to understand what it's doing. **Import APIs and Declare Variables** ```javascript import { currentMember, authentication } from 'wix-members-frontend'; import wixData from 'wix-data'; import wixLocationFrontend from 'wix-location-frontend'; let memberId, memberEmail; ``` **Lines 1-3**: First, we import all the APIs we need for our example: [wix-members-frontend](https://www.wix.com/velo/reference/wix-members): For getting information about the current member and for logging members in and out. [wix-data](https://www.wix.com/velo/reference/wix-data.html): For querying and inserting into the Members collection. [wix-location-frontend](https://www.wix.com/velo/reference/wix-location.html): For navigating to other pages **Line 5**: We declare 2 global variables, `memberId` and `memberEmail`. **Check if the Current Member is Logged In** ```javascript async function checkIfLoggedIn(){ const thisMember = await currentMember.getMember(); return thisMember ? true : false; } ``` **Lines 1-2**: We add an [async](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) function to check if the current member is logged in, and await the current member. **Lines 3-4**: We return the current member. If we can get the current member, `thisMember` is `true`, and if not, then `thisMember` is `false`. **Set Up the 'login/logout' Buttons** ```javascript $w.onReady( () => { if (wixWindowFrontend.rendering.env === "browser") { checkIfLoggedIn().then(res => { if (res === true) { $w("#loginButton").label = "Logout"; $w("#profileButton").show(); } else { $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } }) ``` **Lines 1-3**: In the `onReady()` function, we set the window rendering environment to ‘browser’ to prevent the code in the `onReady()` function from running twice. We then call the `checkIfLoggedIn()` function to see if the current member is logged in. **Lines 4-6**: If the current member is logged in, we label the button ‘Logout’, and show the profile button. **Lines 7-11**: If the current member is not logged in, we label the button ‘Login', and hide the profile button. **Authenticate the Member on Log In** ```javascript authentication.onLogin(async (member) => { const loggedInMember = await member.getMember(); memberId = loggedInMember._id; memberEmail = loggedInMember.loginEmail; return wixData.query("Members") .eq("_id", memberId) .find() .then((results) => { if (results.Members.length === 0) { const toInsert = { "_id": memberId, "email": memberEmail }; wixData.insert("Members", toInsert) .catch( (err) => { console.log(err); } ); } }) .catch( (err) => { console.log(err); }); }); } }); ``` **Lines 1-2**: Still in the `onReady()` function, we add an async function authenticating the member when they log in, and await the current member data. **Lines 3-4**: We set the current member ID and email, returned from the current member data to variables. **Lines 5-25**: We search the ‘Members’ data collection for the current member’s ID. If no results are found, then we insert a new item in the ‘Members’ collection for the new member, with their ID and email. **When the Login Button is Clicked** ```javascript export async function loginButton_click(event) { if(await checkIfLoggedIn() === true) { authentication.logout(); console.log('Member is logged out.') $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } else { authentication.promptLogin({"mode": "login"}) .then(() => { console.log('Member is logged in.'); $w("#loginButton").label = "Logout"; $w("#profileButton").show(); }) .catch((error) => { console.error(error); }); } } ``` **Lines 1-6**: We add an async event handler to the login button. When a member clicks the login button, the function first checks if the member is logged in. If the member is logged in, the button’s label already shows ‘Logout’ from the logic in the `onReady()` function above. So when the member clicks the (logout) button, they are logged out, the button label changes to 'Login', and the profile button is hidden. **Lines 7-18**: If the member is not logged in, the button’s label already shows ‘Login’ from the logic in the `onReady()` function above. So when the member clicks the (login) button, they are prompted to log in, the button label then changes to 'Logout', and the profile button is shown. **When the Profile Button is Clicked** ```javascript export async function profileButton_click(event) { let loggedInMember = await currentMember.getMember(); memberId = loggedInMember._id; wixLocationFrontend.to(`/items/${memberId}`); } ``` **Lines 1-2**: We add an async event handler to the profile button. When a member clicks the profile button, the function first gets the current member data. **Lines 3-5**: We set the current member ID, returned from the current member data to a variable. We pass the variable to the `wix-location-frontend.to()` function, sending members to their personal profile page. * * * **Full Example Code** Here is the complete code for this example: ```javascript import { currentMember, authentication } from 'wix-members-frontend'; import wixData from 'wix-data'; import wixLocationFrontend from 'wix-location-frontend'; let memberId, memberEmail; async function checkIfLoggedIn(){ const thisMember = await currentMember.getMember(); return thisMember ? true : false; } $w.onReady( () => { if (wixWindowFrontend.rendering.env === "browser") { checkIfLoggedIn().then(res => { if (res === true) { $w("#loginButton").label = "Logout"; $w("#profileButton").show(); } else { $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } }) authentication.onLogin(async (member) => { const loggedInMember = await member.getMember(); memberId = loggedInMember._id; memberEmail = loggedInMember.loginEmail; return wixData.query("Members") .eq("_id", memberId) .find() .then((results) => { if (results.Members.length === 0) { const toInsert = { "_id": memberId, "email": memberEmail }; wixData.insert("Members", toInsert) .catch( (err) => { console.log(err); } ); } }) .catch( (err) => { console.log(err); }); }); } }); export async function loginButton_click(event) { if(await checkIfLoggedIn() === true) { authentication.logout(); console.log('Member is logged out.') $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } else { authentication.promptLogin({"mode": "login"}) .then(() => { console.log('Member is logged in.'); $w("#loginButton").label = "Logout"; $w("#profileButton").show(); }) .catch((error) => { console.error(error); }); } } export async function profileButton_click(event) { let loggedInMember = await currentMember.getMember(); memberId = loggedInMember._id; wixLocationFrontend.to(`/items/${memberId}`); } ``` To learn more about the [wix-members-frontend module](https://www.wix.com/velo/reference/wix-members), see the API Reference. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Enabling Custom Site Registration ## Article: Enabling Custom Site Registration ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/enabling-custom-site-registration.md ## Article Content: # Velo: Enabling Custom Site Registration Velo allows you to [create a custom site registration form](https://support.wix.com/en/article/how-to-create-a-custom-registration-form) using code.  After creating a custom site registration form, you must enable custom site registration so your visitors will be directed to your custom form. > **Note:** > To learn about creating a custom member signup form without code, click [here](https://support.wix.com/en/article/creating-a-custom-signup-form-for-your-members-area). ### To enable custom site registration using Velo forms: 1. Add a popup to your site. 2. [Create a custom registration form](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/creating-a-custom-registration-form-with-code.md) in the popup using Velo. >**Notes:** > - The above tutorial uses the [frontend `register()`](https://www.wix.com/velo/reference/wix-members-frontend/authentication/register) function. To enable custom site registration using Velo forms, use the [backend `register()`](https://www.wix.com/velo/reference/wix-members-backend/authentication/register) function. > - Check [your member signup settings](https://support.wix.com/en/article/editing-your-member-signup-settings-for-the-default-form#editing-the-member-signup-settings-for-the-custom-signup-form) when creating the form, specifically whether a new member may join and log in immediately, or they need to wait for a manual approval. 3. Enable Velo Form signup settings: 1. Click **Pages** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/030571df-9516-496b-bc2a-2a8af88f13dd/2022/02/02/0e066fda-f14d-4190-b4de-70959d4f70ba/db90c9fb-861c-40a8-9889-e06dbddd6da4.png) on the left side of the Editor. 2. Click **Signup & Login**. 3. Click **Member Signup Form** (Wix Editor), or **Signup** (Wix Studio). 4. Click the drop-down menu and select **Velo Form**. Note that for this option to be available, you need to enable **Dev Mode** (Wix Editor), or click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding** (Wix Studio). 5. Choose the popup from the **What does it link to?** drop-down menu. 6. Select **Advanced Settings** and enable **Only allow backend calls for Signup and Login APIs**. To enable this setting, note that you must complete steps 4 and 5 for the **Member Login Form** (located under the **Login** tab in Wix Studio). This is an important step to ensure that site visitors can't verify themselves by modifying page code. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Processing Payments ## Article: Processing Payments ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-processing-payments.md ## Article Content: # Velo Tutorial: Processing Payments Using the Velo Pay API you can collect payments from your site's visitors outside the context of a Wix App like Wix Stores. The API allows you to collect a payment when triggered by any user interaction on any page.
**Warning:** Before starting with payments, it is important that you fully understand how to handle the security concerns detailed below that arise when collecting payments.
### Prerequisites Before using the Pay API, you need to set up your site to accept payments. To learn more, see [About Accepting Payments](https://support.wix.com/en/article/about-accepting-payments).
**Note:** When setting up your site to accept payments, be sure to select the payment methods you want to offer and [set your payment currency](https://support.wix.com/en/article/setting-your-currency-for-accepting-payments).
Additionally, you will need to be familiar with creating backend web modules and calling the functions defined in them from page code. To learn more, see [Calling Server-Side Code from the Front-End with Web Modules.](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) You also might want to familiarize yourself with who can see and call your site's code. To learn more, see the [Code Visibility](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md) section of [Security Considerations When Working with Velo](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md). ### Pay API
**Note:** To work with the Pay API, you need to save and publish your site.
The Pay API is split between backend and client-side functions to facilitate a secure payment process as described [below](https://support.wix.com/en/article/how-to-process-payments-with-code#security-considerations). It consists of two functions and one event: * `createPayment()` - Called in backend code to create a payment and generate a `paymentId`. * `startPayment()` - Called in client-side code to prompt the current site visitor to enter the payment information. * `onPaymentUpdate()` - Fired in backend code when a payment's status has changed. For detailed information on the Pay API see [wix-pay-frontend](https://dev.wix.com/docs/velo/apis/wix-pay-frontend/introduction.md) and [wix-pay-backend](https://dev.wix.com/docs/velo/apis/wix-pay-backend/introduction.md) in the API Reference. ### Payment Lifecycle The following list outlines the steps taken in a typical payment lifecycle: 1. A site visitor clicks a button to start the payment process. 2. The button's event handler calls a backend function. 3. A `PaymentInfo` object containing information about the payment, such as the payment amount, is created in the backend function. 4. The backend function calls `createPayment()` using the `PaymentInfo` object and returns the generated `Payment` object to the calling client-side event handler. 5. The event handler then calls the `startPayment()` function with the `id` from the `Payment` object, which opens the payment popup on your site. 6. The site visitor enters the payment information. 7. The event handler optionally handles the returned `PaymentResult`. 8. Handle additional status updates to the payment transaction using the `onPaymentUpdate()` event. > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. ### Payment Code Example The following is an example showing the payment lifecycle in code. The numbered comments correspond to the numbered steps in the payment lifecycle described above. ```javascript /******************** * client-side code * ********************/ import { createMyPayment } from 'backend/pay.web'; import wixPayFrontend from 'wix-pay-frontend'; import wixWindowFrontend from 'wix-window-frontend'; // Step 1 - User clicks a button. export function myButton_click(event) { // Step 2 - Call backend function. // (Next, see step 3 in the backend code below.) createMyPayment() // When the payment has been created and a paymentId has been returned: .then( (payment) => { // Step 5 - Call the startPayment() function with the paymentId. // Include PaymentOptions to customize the payment experience. wixPayFrontend.startPayment(payment.id, { "showThankYouPage": false, "termsAndConditionsLink": "https://mysite.com/terms" }) // Step 6 - Visitor enters the payment information. // When the payment form is completed: .then( (result) => { // Step 7 - Handle the payment result. // (Next, see step 8 in the backend code below.) if (result.status === "Successful") { wixWindowFrontend.openLightbox("Success Box"); } else if (result.status === "Pending") { wixWindowFrontend.openLightbox("Pending Box"); } } ); } ); } ``` ```javascript /************************** * backend code - pay.web.js * **************************/ import { Permissions, webMethod } from 'wix-web-module'; import wixPay from 'wix-pay-backend'; export const createMyPayment = webMethod(Permissions.Anyone, () => { // Step 3 - Create payment info object. // Here we use static data. You might want to use data from a // collection. To see an example of such a usage, see the API Reference. let paymentInfo = { "items": [ { name: "Product 1", price: 9.99 }, { name: "Product 2", price: 19.99 } ], amount: 29.98 } // Step 4 - Call createPayment() with the payment information // and return the paymentId. // (Next, see step 5 in the client-side code above.) return wixPay.createPayment(paymentInfo); }); ``` ```javascript /***************************** * backend code - events.js * *****************************/ export function wixPay_onPaymentUpdate(event) { // Step 8 - Handle additional status updates using // the onPaymentUpdate() event. let paymentId = event.payment.id; let newTransactionStatus = event.status; let userInfo = event.userInfo; // Handle new payment status. } ``` ### Security Considerations For security reasons you should always create the `PaymentInfo` object (step 3 above) in backend code. **Do not pass payment information from client-side code.** Passing payment information from client-side code opens a vulnerability which is easily exploited. Since malicious site visitors can call your exported backend functions from the browser, they would be able to alter the payment information that is passed to the backend code. For example, if you pass the price of an item being purchased from client-side code, a user can change the price of the item. To securely process a payment, always define the payment information in the backend. Even though malicious users can call the backend function, they cannot see or change what happens in that function. If needed, you can also include additional validations in your backend function. If you want to perform operations based on a payment's status, always use the status updates received by the backend payment events. Status information that is received in client-side code should only be used for display purposes. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Using the Velo Pay API to Collect Payments for a Single Product ## Article: Using the Velo Pay API to Collect Payments for a Single Product ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-a-single-product.md ## Article Content: # Velo Tutorial: Using the Velo Pay API to Collect Payments for a Single Product This article describes how you can use the Velo Pay API to collect payments from your site's visitors for a single predefined product, outside the context of a Wix App (like Wix Stores). This article is based on this [Wix Editor example site](https://velo-examples.wixsite.com/toystore). You can open the site in the Wix Editor to work with the [template](http://editor.wix.com/html/editor/web/renderer/new?siteId=5eb2ea3b-6527-4b5e-899a-bbf130adfab0&metaSiteId=146f29a1-b47c-4a03-acac-eb05894259ec). >**Note:** > The example site and template are built on Wix Editor sites. This tutorial will be slightly different from the example site and template as its steps have been updated for a Wix Studio site with the [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). To learn how to use the Velo Pay API for products stored in a collection, see [Velo: Using the Wix Pay API to Collect Payments for Products in a Database Collection](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-products-in-a-database-collection.md).
### Overview **In our site we added the following:** * Text elements and an image on the Pay API page, displaying a single product for sale. * A "Buy Now" button on the Pay API page, which visitors can click to purchase the product. **Then we added code to do the following:** 1. When a visitor clicks the "Buy Now" button, the button's event handler calls a backend function. 2. The backend function creates and returns a payment object to the client side. The payment object contains payment information for the product as defined in the backend code. 3. On the client side, a payment procedure is initiated using the ID from the payment object, causing a payment window to appear. 4. The visitor enters payment information and completes the transaction. ### Before You Start Before you start working with Wix payments in code, make sure you do the following: * Understand the [necessary security precautions](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-processing-payments.md) for working with payments in code. Specifically, make sure to always define payment information in the backend. This prevents malicious users from accessing and possibly manipulating payment information or the payment process. * Set up [accepted payment methods](https://support.wix.com/en/article/about-accepting-payments) on your site. We also recommend that you understand the [typical payment process](https://www.wix.com/velo/reference/wix-pay.html) before proceeding. ### Step 1: Set up the Pay API Page On the Pay API page we added: * An image of the robot for sale. * Text elements for the name, price, and description of the robot. * A Buy Now button to trigger the payment process. In the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/adding-custom-interactivity-with-events.md) of the button, we added an onClick [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md). ### Step 2: Create the createPaymentForProduct Function in the Backend We created a web module called `BE_PayAPI.web.js`. Then we imported the function that we need to work with payments in backend code, as well as the `webMethod` function and the `Permissions` enum from the `wix-web-module` module. Then we declared the `createPaymentForProduct` function, which creates and returns a payment object. We also export the function so it can be used on the client side. Note that `amount` is the sum of the `price` property for all `items`. In this example, there is only one `item` so `amount` and `price` are identical. ```javascript import {Permissions, webMethod} from "wix-web-module"; import wixPayFrontend from 'wix-pay-backend'; export const createPaymentForProduct = webMethod(Permissions.Anyone, () => { return wixPayFrontend.createPayment( { amount: 0.50, items: [{name: 'DIY Robot', price: 0.50}], } ); }) ``` #### Understanding the Code **Line 1**: Import the `webMethod` function and the `Permissions` enum from the `wix-web-module` module. **Line 2**: Import the module we need to work with the [Wix Pay Backend](https://www.wix.com/velo/reference/wix-pay-backend.html) library. **Line 4**: Declare the `createPaymentForProduct` function, and export it with the `webMethod` function and `Permissions.Anyone` so it can be used on the client side. In the function, do the following: **Line 5**: Return the result of the [`wix-backend-pay`](https://www.wix.com/velo/reference/wix-pay-backend.html) [`createPayment`](https://www.wix.com/velo/reference/wix-pay-backend.html#createPayment) function, which takes a [`PaymentInfo`](https://www.wix.com/velo/reference/wix-pay-backend/introduction) object and creates a new payment object. **Lines 6-9**: Define the `PaymentInfo` (product payment information) directly in the code. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify these items to match the ones on your site: * `0.50` (`amount`) * `DIY Robot` (`name`) * `0.50` (`price`) ### Step 3: Prepare the Pay API Page On the Pay API page, we start by importing the module we need to work with payments in code. We also import the `createPaymentForProduct` function from the backend. ```javascript import wixPayFrontend from 'wix-pay-frontend'; import {createPaymentForProduct} from 'backend/BE_PayAPI.web'; ``` #### Understanding the Code **Line 1**: Import the module we need to work with the [Wix Pay Frontend](https://www.wix.com/velo/reference/wix-pay) library. **Line 2**: Import the `createPaymentForProduct` function from the web method where it was created (see [Step 2](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-a-single-product.md)). This function creates a payment object based on payment information for a single product defined in the backend. There are no identifiers you would need to change here to make this code work on your site. ### Step 4: Create the button1\_click function on the Pay API Page The `button1_click` [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) runs when a visitor clicks the Buy Now button. The event handler calls backend code that returns a payment object based on product payment information. The event handler then runs the wixPayFrontend [`startPayment`](https://www.wix.com/velo/reference/wix-pay.html#startPayment) function with the payment object returned from the backend. This opens a payment window and prompts the user to select a payment method and continue the payment process. In our example, we included a `termsAndConditionsLink`, one of the [payment options](https://www.wix.com/velo/reference/wix-pay/startpayment) available for payments. ```javascript export function button1_click(event) { createPaymentForProduct().then(payment => { wixPayFrontend.startPayment(payment.id, {"termsAndConditionsLink": "https://www.wix.com/"}); }); } ``` #### Understanding the Code **Line 1**: When the "Buy Now" button is clicked, run an event handler that does the following: **Line 2**: Run the `createPaymentForProduct` function, which was imported from the backend. With the payment object that is returned, do the following: **Line 3**: Run the wixPayFrontend [`startPayment`](https://www.wix.com/velo/reference/wix-pay.html#startPayment) function with the ID of the payment object. A payment window opens prompting the user for payment information. The `startPayment` function runs with an optional `termsAndConditionsLink` [`PaymentOption`](https://www.wix.com/velo/reference/wix-pay/startpayment). A checkbox with a link to a terms and conditions page is included in the payment window. There are no identifiers you would need to change here to make this code work on your site. In the payment window, the site visitor selects a payment method, fills in payment information, clicks **Pay Now**, and the transaction is completed. The visitor receives an email confirming the payment. ### Next Steps * Open [this example](http://editor.wix.com/html/editor/web/renderer/new?siteId=5eb2ea3b-6527-4b5e-899a-bbf130adfab0&metaSiteId=146f29a1-b47c-4a03-acac-eb05894259ec) in the Editor to work with the template. * Publish the site. * Learn more: * [Velo: Using the Wix Pay API to Collect Payments for Products in a Database Collection](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-products-in-a-database-collection.md) * [Velo Tutorial: Processing Payments](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-processing-payments.md) * [wix-pay-frontend API](https://www.wix.com/velo/reference/wix-pay.html) * [wix-pay-backend API](https://www.wix.com/velo/reference/wix-pay-backend.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Using the Velo Pay API to Collect Payments for Products in a Database Collection ## Article: Using the Velo Pay API to Collect Payments for Products in a Database Collection ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-products-in-a-database-collection.md ## Article Content: # Velo Tutorial: Using the Velo Pay API to Collect Payments for Products in a Database Collection >**Note:** This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). This article describes how you can use the Velo Pay API to collect payments from your site's visitors for a product stored in a database collection, outside the context of a Wix App (like Wix Stores). Throughout this article we're going to use the same site to illustrate the process. You can open the site in the Editor to work with the [template](http://editor.wix.com/html/editor/web/renderer/new?siteId=712f4cc1-af6b-4223-a799-085d304242d4&metaSiteId=a9b9d72c-ee33-48e3-9b4a-1427aa561c50). We're going to explain how we set up the sample site and the code we added to make it work.
>**Note** > To learn how to use the Velo Pay API for a single predefined product, see [Velo: Using the Velo Pay API to Collect Payments for a Single Product](https://support.wix.com/en/article/velo-tutorial-using-the-velo-pay-api-to-collect-payments-for-a-single-product). ### Overview **In our site we added the following:** * A Products collection with a list of products for sale. Each product needs a title, image, and price. * A Collection & Repeater page. A repeater displays products from the Products collection. Each product in the repeater has a Buy Now button, which visitors can click to purchase the product. **Then we added code to do the following:** 1. When a visitor clicks the Buy Now button, the button's event handler calls a backend function. 2. The backend function retrieves product payment information from the Products collection, and creates and returns a payment object to the client side. 3. On the client side, a payment procedure is initiated using the ID from the payment object, causing a payment window to appear. 4. The visitor enters payment information and completes the transaction. ### Before You Start Before you start working with Wix payments in code, make sure you do the following: * Understand the [necessary security precautions](https://support.wix.com/en/article/velo-tutorial-processing-payments#security-considerations) for working with payments in code. Specifically, make sure to: * Always define payment information in the backend.  * Assign [collection permissions](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) that protect payment information stored in your database. Make sure only site Admins can create, update, and delete content. * Set up [accepted payment methods](https://support.wix.com/en/article/about-accepting-payments) on your site. We also recommend that you understand the [typical payment process](https://www.wix.com/velo/reference/wix-pay.html) before proceeding. ### Step 1: Set up the Site For this example, you'll need some products for sale that are stored in a collection. We added Wix Stores to our site, which automatically adds a Stores/Products collection, but you can also create your own collection of products without Wix Stores. >**Note** > You may need to save or publish the site and refresh your browser to view Stores collections in Content Collections in the Databases section of the Code sidebar. This is what some of the data in our Products collection looks like:
We also added a Collection & Repeater page to the site to display our products, and we created the BE\_Collection.web.js backend module to securely run code related to payment information.
### Step 2: Set up the Collection & Repeater Page On the Collection & Repeater page we added: * A repeater to display the products. * Text and image elements in the repeater to display the name, price, and picture of each product for sale. * A Buy Now button in the repeater to trigger the payment process for the selected product. * A dataset connected to our Stores/Products collection for connecting data in the collection to the repeater.
>**Note** > When you [display data from a collection in a repeater](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md), you must first connect the repeater to the dataset, and then connect each element in the repeater to the dataset. * In the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/adding-custom-interactivity-with-events.md) of the repeater, we added an [onItemReady](https://www.wix.com/velo/reference/$w.Repeater.html#onItemReady) [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) that will run when the repeater is ready to be loaded. * In the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/adding-custom-interactivity-with-events.md) of the Buy Now button, we added an [onClick](https://dev.wix.com/docs/velo/api-reference/$w/button/on-click.md) [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) that will run when the button is clicked. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2020/10/25/db68d0e6-ad19-4040-92ce-24e4c4dbeecd/a15a0149-a856-46f9-adcd-8bc0c2472387.png) ### Step 3: Create the createPaymentForProduct Function in the Backend We created a backend module called `BE_Collection.web.js`. In the backend, we start by importing the modules we need to work with backend payments and data in code. Then we created the `createPaymentForProduct` function, which retrieves product data from the collection, and creates and returns a payment object based on the data. We also export the function so it can be used on the client side. Note that `amount` is the sum of the `price` property for all `items`. In this example, there is only one `item` so `amount` and `price` are identical. >**Note:** > Throughout this example we use the [async/await](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) JavaScript functionality. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import wixPay from 'wix-pay-backend'; import wixData from 'wix-data'; export const createPaymentForProduct = webMethod(Permissions.Anyone, async (productId) => { let product = await wixData.get('Stores/Products', productId); return wixPay.createPayment( { amount: product.discountedPrice, items: [ { name: product.name, price: product.discountedPrice } ] } ); }); ``` #### Understanding the Code **Line 1**: Import the `Permissions` enum and `webMethod` function from `wix-web-module`. **Line 2**: Import the module we need to work with the [Wix Pay Backend](https://www.wix.com/velo/reference/wix-pay-backend.html) library. **Line 3**: Import the module we need to work with the [Wix Data](https://www.wix.com/velo/reference/wix-data.html) library. **Line 5**: Declare the `createPaymentForProduct` function and export it so it can be used on the client side. **Line 6**: Use the Wix Data [`get`](https://www.wix.com/velo/reference/wix-data.html#get) function to retrieve product information from the collection and assign it to the `product` variable. The `get` function uses the ID of the product selected by the visitor to "know" which product data to retrieve. **Line 7**: Return the result of the [`wix-pay-backend`](https://www.wix.com/velo/reference/wix-pay-backend.html) [`createPayment`](https://www.wix.com/velo/reference/wix-pay-backend.html#createPayment) function, which takes a [`PaymentInfo`](https://www.wix.com/velo/reference/wix-pay-backend/introduction) object and creates a new payment object. **Lines 8-10**: Define the `PaymentInfo` object using payment information stored in the `product` variable. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify this item to match the one on your site: * `Stores/Products` ### Step 4: Prepare the Collection & Repeater Page On the Collection & Repeater page we start by importing the module we need to work with payments in code. We also import the `createPaymentForProduct` function from the backend. ```javascript import wixPayFrontend from 'wix-pay-frontend'; import {createPaymentForProduct} from 'backend/BE_Collection.web'; ``` #### Understanding the Code **Line 1**: Import the module we need to work with the [Wix Pay Frontend](https://www.wix.com/velo/reference/wix-pay) library. **Line 2**: Import the `createPaymentForProduct` function from the backend module where it was created (see [Step 3](https://support.wix.com/en/article/velo-tutorial-using-the-velo-pay-api-to-collect-payments-for-products-in-a-database-collection#step-3-create-the-createpaymentforproduct-function-in-the-backend-1)). This function creates a payment object based on payment information for a product stored in the Products collection. There are no identifiers you would need to change here to make this code work on your site. ### Step 5: Create the repeater1\_itemReady function on the Collection & Repeater page The `repeater1_itemReady` [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) runs when the repeater is ready to be loaded. The function runs for each item (product) in the collection. The current item is passed to the event handler as the `itemData` parameter. First the function loads the item into the repeater, displaying product information. Then it checks if the user clicked the Buy Now button for this particular item. If the button for this item was clicked, the `onClick()` [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) runs the backend `createPaymentForProduct` function that creates a payment object for the selected product. The event handler then runs the wixPayFrontend [`startPayment`](https://www.wix.com/velo/reference/wix-pay.html#startPayment) function with the payment object returned from the backend. This opens a payment window and prompts the user to select a payment method and continue the payment process. In our example, we also included a `termsAndConditionsLink`, one of the available [payment options](https://www.wix.com/velo/reference/wix-pay/startpayment). ```javascript export function repeater1_itemReady($item, itemData, index) { let itemId = itemData._id; $item('#button1').onClick(async () => { let payment = await createPaymentForProduct(itemId); await wixPayFrontend.startPayment((payment.id), { "termsAndConditionsLink": "https://www.wix.com/" }); }); } ``` #### Understanding the Code **Line 1**: For each item (product) in the Stores/Products dataset, set up the item in the repeater and then do the following: **Line 2**: Store the ID of the item in the `itemID` variable. **Line 3**: If the "Buy Now" button is clicked for this item, run an event handler that does the following: **Line 4**: Run the `createPaymentForProduct` function, which was imported from the backend, with the ID of the selected product. Store the payment object that is returned in the `payment` variable. **Line 5**: Run the Wix Pay Frontend [`startPayment`](https://www.wix.com/velo/reference/wix-pay.html#startPayment) function with the ID of the payment object. A payment window opens prompting the user for payment information. The `startPayment` function runs with an optional `termsAndConditionsLink` [`PaymentOption`](https://www.wix.com/velo/reference/wix-pay/startpayment). A checkbox with a link to a terms and conditions page is included in the payment window. #### Identifiers you may need to change based on your site's elements If you want to use this exact scenario and code in your site, you may need to modify this item to match the one on your site: * `#button1` In the payment window, the site visitor selects a payment method, fills in payment information, clicks **Pay Now**, and the transaction is completed. The visitor receives an email confirming the payment. ### Next Steps * Open [this example](http://editor.wix.com/html/editor/web/renderer/new?siteId=712f4cc1-af6b-4223-a799-085d304242d4&metaSiteId=a9b9d72c-ee33-48e3-9b4a-1427aa561c50) in the Editor to work with the template. * Publish the site. * Learn more: * [Velo: Using Wix Pay API to Collect Payments for a Single Product](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-using-the-velo-pay-api-to-collect-payments-for-a-single-product.md) * [Velo Tutorial: Processing Payments](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-processing-payments.md) * [wix-pay-frontend API](https://www.wix.com/velo/reference/wix-pay.html) * [wix-pay-backend API](https://www.wix.com/velo/reference/wix-pay-backend.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Payment Provider Service Plugin ## Article: Payment Provider Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-pay/tutorial-payment-provider-service-plugin.md ## Article Content: # Velo Tutorial: Payment Provider Service Plugin > **Before getting started** > > In this tutorial, you'll learn how to write the code to integrate a new payment provider with your site. If you're not a developer, check out these helpful links: > * [Available Payment Providers in Your Country](https://support.wix.com/en/article/available-payment-providers-in-your-country) > * [Requesting Additional Payment Providers & Methods](https://support.wix.com/en/article/requesting-additional-payment-providers-methods) > * [Hire a Developer for Your Project](https://www.wix.com/velo/hire-a-developer) Wix service plugins (formerly SPIs and custom extensions) allow you to expand what your site can do by integrating with 3rd-party services not currently supported by Wix. They also allow you to implement custom logic to change how your site displays and behaves using Velo. Learn more about [service plugins](https://support.wix.com/en/article/velo-custom-business-app-extensions-using-spis-beta). You can manage service plugins from your site's dashboard, and they behave just like the ones Wix already supports. With the payment provider service plugin, you can integrate your site with payment providers that aren't currently supported by Wix. You can also customize the transaction and refund flows to match your site's specific needs. This guide explains how to set up a payment provider service plugin on your site using Velo. The process has 3 steps: 1. Create a new payment provider plugin on your site. 2. Implement your plugin with custom code. 3. Deploy the plugin. > > **Note:** > You can only use this service plugin to integrate payment providers with your own sites. You can't publish the plugin you create on the [Wix App Market](https://www.wix.com/app-market/). If you are a payment service provider who wants to make your services available on Wix sites, use the REST [Payment Service Provider service plugin](https://dev.wix.com/docs/rest/business-management/payments/service-plugins/payment-service-provider-service-plugin/introduction.md). ## Step 1: Create a new payment provider plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Code sidebar that contains the files for your code. 1. With [Wix developer mode](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md) enabled, click **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/1c4d0337-43f1-4f62-a125-758ae18c0228/618cf4f4-ac25-4ab2-8998-d3f511c91d30.png) on the Code sidebar. 2. Scroll down to **Service Plugins**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3b28ebcfbf8f2b6165f835d528f2f55c.png) 3. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png) . 4. Select **Payment**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/08e20148dd45b2dd641501f937cedd71.png) 5. Enter a name for your plugin and click **Add & Edit Code**. The name can't contain spaces or special characters. ## Step 2: Implement the plugin The procedure in the previous section creates a folder in the **Service Plugins** section of the Code sidebar called **payment-provider**. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, **-config.js** and **.js**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/861431e65a6fd40b97725e76e5cf47b8.png) To implement your plugin, you need to: * Add custom code in these files to integrate with a payment provider. * Expose HTTP endpoints on your site that the payment provider can use to send your site updates about transactions and refunds. Here are some guidelines for writing your code: ### \-config.js The code in this file defines a function named `getConfig()` that returns an object containing the values used to display information about your plugin in 3 different locations on your site: 1. The **Connect ** page where you connect a payment provider to your site. 3. The **Accept Payments** page where you see the payment methods you've connected to your site. 5. The **Checkout** page where your site visitors finalize their shipping and payment details and place orders. The object returned by `getConfig()` includes the following properties: * **`title`**: (required) The name of the payment service provider as a string. This name is displayed when selecting payment providers to connect to on your site's dashboard. * **`paymentMethods`**: (required) An array of `paymentMethod` objects defining the different payment methods supported by this provider. Each payment method object has a key whose value is the name of payment method. For now, only `'hostedPage'` is supported. The value of this key is an object that can include the following properties: * **`title`**: (required) The payment method title as a string. This title is used to display the payment method on your site's dashboard. * **`logos`**: An object containing URLs to the payment method's logo as strings. These logos are displayed on the site's dashboard and the checkout cart. You can provide different logos for white and colored backgrounds. See the example return object below for details. * **`billingAddressMandatoryFields`**: An array of strings representing the billing address fields that buyers must include in order to use this payment method. Options: `'ZIPCODE'`, `'CITY'`, `'STATE'`, `'ADDRESS'`, `'COUNTRY_CODE'`, `'EMAIL'`, `'PHONE'`, `'FIRST_NAME'`, `'LAST_NAME'`, `'STREET'`, `'HOUSE_NUMBER'`, `'TAX_IDENTIFIER'` * **`credentialsFields`**: (at least 1 credentials object is required) An array of objects representing the credentials required to connect an account for the payment provider. The credentials fields are displayed on the **Connect ** page in your site's dashboard. The following credentials objects are supported: * **`simpleField`**: An object representing a text field. * **`name`**: The key to use for the field in your code as a string. * **`label`**: The label of the field to display on the dashboard as a string. * **`checkboxField`**: An object representing a checkbox field. * **`name`**: The key to use for the field in your code as a string. * **`label`**: The label of the field to display on the dashboard as a string. * **`tooltip`**: Tooltip text to display when the user hovers over the field as a string. * **`dropdownField`**: An object representing a dropdown field. * **`name`**: The key to use for the field in your code as a string. * **`label`**: The label of the field to display on the dashboard as a string. * **`options`**: An array of objects representing the options to display in the dropdown field. Each options object contains the following properties: * **`key`**: The name of the option to use in your code as a string. * **`value`**: The label of the option to display on the dashboard as a string. Example return object: ```javascript { title: 'Banana Payments', paymentMethods: [{ hostedPage: { title: 'Banana Payments', billingAddressMandatoryFields: ['CITY'], logos: { white: { svg: 'https://static.wixstatic.com/shapes/94b5e2_403ceb582027431cb92f38fd18d1843c.svg', png: 'https://freesvg.org/img/15930333081593032446pitr_Bananas_icon.png' }, colored: { svg: 'https://static.wixstatic.com/shapes/94b5e2_403ceb582027431cb92f38fd18d1843c.svg', png: 'https://freesvg.org/img/15930333081593032446pitr_Bananas_icon.png' } } } }], credentialsFields: [{ simpleField: { name: 'clientId', label: 'API id' } }, { simpleField: { name: 'clientSecret', label: 'API secret' } }] } ``` ### \.js The code in this file defines the [`connectAccount()`](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#connectaccount), [`createTransaction()`](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#createtransaction) and [`refundTransaction()`](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#refundtransaction) functions. These functions are called by Wix at different points in the payment processing flow. The following sections describe the usage, parameters, and return values of these functions. #### connectAccount() This function is called when you click the **Connect** button on the **Connect ** page in your site's dashboard. You can use it to create a new account for the payment provider you want to connect to, and return the account information to Wix. The function accepts the following parameters: `options`: An object containing the credentials you entered on the **Connect ** page together with other information about the site that you might need to connect to the payment provider. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/connectaccount). Example options object: ```javascript { credentials: { client_id: 'da5a42c8914a-568-4d-5f25b-44438d6a', client_secret: '38d6a2c5f25b8-568-4d-914a-da5a4444' }, country: 'US', currency: 'USD', wixMerchantId: '3e01ddt8-134e-4dd7-a28f-ht6jf276c876' } ``` > > **Note:** > This function has a second parameter called `context`. This parameter is for internal Wix use only. You don't need to use it in your code. The `createAccount()` function must return an object containing the account information and credentials for the payment provider you're connecting to. Wix uses the return values from this function to set up the payment provider on your site. The account credentials returned by this function are also passed to your plugin's [`createTransaction()`](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#createtransaction) and [`refundTransaction()`](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#refundtransaction) functions. This way, you can use them to make requests to the payment provider's API. If the payment provider fails to create an account, return an error object instead of the account details. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/connectaccount). Example return value: ```javascript { credentials: { client_id: 'da5a42c8914a-568-4d-5f25b-44438d6a', client_secret: '38d6a2c5f25b8-568-4d-914a-da5a4444' }, accountId: '1o3k5234-2342kk334', accountName: 'john@doe.com' } ``` #### createTransaction() This function is called when a site visitor clicks the **Place Order** button on your site's **Checkout** page. Use it to send a request to a payment provider to create a new transaction. The function accepts the following parameters: `options`: An object containing information about your site, the site visitor's order, and redirect URLs for payment processing. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/createtransaction). Example options object: ```json { "wixTransactionId": "6b264d6f-f54e-426a-a8ed-b6f103c86d46", "paymentMethod": "385f25b8-79c0-4d44-914a-d6568a2cda5a", "merchantCredentials": { "client_id": "da5a42c8914a-568-4d-5f25b-44438d6a", "client_secret": "38d6a2c5f25b8-568-4d-914a-da5a4444" }, "order": { "_id": "fb591a7c-8471-464a-a64c-181c449b07ed", "description": { "billingAddress": { "city": "New York", "email": "jane@doe.com", "state": "NY", "zipCode": "10014", "lastName": "Jane", "firstName": "Doe", "countryCode": "US", "address": "100 Gansevoort St", "phone": "212-555-0100" }, "items": [ { "_id": "baeb40f5-a48a-4424-cec2-253c9772ad42", "name": "Banana shirt", "quantity": 1, "price": "10000" } ], "totalAmount": "10000", "charges": {}, "buyerInfo": { "buyerId": "9b5d1bed-6a1d-4980-9d2e-82713fb6bf0e", "buyerLanguage": "en" }, "currency": "EUR", "shippingAddress": { "city": "New York", "email": "jane@doe.com", "state": "NY", "zipCode": "10014", "lastName": "Jane", "firstName": "Doe", "countryCode": "US", "address": "100 Gansevoort St", "phone": "212-555-0100" } }, "returnUrls": { "successUrl": "https://cashier-services.wix.com/_api/payment-services-web/redirect/success/b5f106b4-f78e-426a-a8ed-3c76d4696d6f", "errorUrl": "https://cashier-services.wix.com/_api/payment-services-web/redirect/error/b5f106b4-f78e-426a-a8ed-3c76d4696d6f?pmName=EmptyPayment&pmUrl=&failureCode=6000", "cancelUrl": "https://cashier-services.wix.com/_api/payment-services-web/redirect/cancel/b5f106b4-f78e-426a-a8ed-3c76d4696d6f", "pendingUrl": "https://cashier-services.wix.com/_api/payment-services-web/redirect/pending/b5f106b4-f78e-426a-a8ed-3c76d4696d6f" } }, "wixMerchantId": "ed2bfda6-134e-4cc9-a37f-756c6843e01d", "fraudInformation": { "remoteIp": "145.231.63.45" } } ``` > > **Note:** > This function has a second parameter called `context`. This parameter is for internal Wix use only. You don't need to use it in your code. The `createTransaction()` function must return an object with a `pluginTransactionId` property that represents payment provider's ID for the created transaction. Other properties may be included depending on the type of transaction you're creating and the results of the request. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/createtransaction). Example return value: ```json { "pluginTransactionId": "e89b-12d3-a456-42665", "redirectUrl": "https://www.example.com/redirect" } ``` To receive updates from the payment provider about the success or failure or the transaction, [expose an HTTP endpoint](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#http-endpoints) for this purpose. Provide the URL to the payment provider as part of the request to create the transaction. Use the [submitEvent()](https://dev.wix.com/docs/velo/apis/wix-payment-provider-backend/submit-event.md) function to in the logic for your endpoint to mark the transaction as completed. Once this happens, the order associated with the transaction appears in the [Orders](https://www.wix.com/my-account/site-selector/?buttonText=Manage%20Orders&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/store/orders) tab in your site's dashboard. #### refundTransaction() This function is called when you [initiate the refund](https://support.wix.com/en/article/refunding-customers-cancelling-orders) of a transaction from your connected payment provider on the [Orders](https://www.wix.com/my-account/site-selector/?buttonText=Manage%20Orders&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/store/orders) tab in your site's dashboard. Use this function to send a request to the payment provider to create a refund for the transaction. The function accepts the following parameters: `options`: An object containing information about the transaction being refunded and the refund amount. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/refundtransaction). Example options object: ```json { "wixTransactionId": "6b264d6f-f54e-426a-a8ed-b6f103c86d46", "pluginTransactionId": "e89b-12d3-a456-42665", "wixRefundId": "4j7lk09d-0ok8-edh5-hj89-1kg9ds8n50lo9", "merchantCredentials": { "client_id": "da5a42c8914a-568-4d-5f25b-444" }, "refundAmount": 10000, "reason" : "REQUESTED_BY_CUSTOMER" } ``` > > **Note:** > This function has a second parameter called `context`. This parameter is for internal Wix use only. You don't need to use it in your code. The `refundTransaction()` function must return an object with a `pluginRefundId` property that represents the ID of the refund created by the payment provider. If the request to the payment provider isn't approved, the return object should include information about why this happened. For more details, see the [service plugin reference](https://www.wix.com/velo/reference/spis/wix-payments/payment-provider/refundtransaction). Example return value for a successful refund: ```json { "pluginRefundId": "e89b-12d3-a456-42665" } ``` Example return value for a failed refund: ```json { "pluginRefundId": "e89b-12d3-a456-42665", "reasonCode": 3025, "errorCode": "INSUFFICIENT_FUNDS_FOR_REFUND", "errorMessage": "Insufficient funds for refund" } ``` To receive updates from the payment provider about the refund, [expose an HTTP endpoint](https://support.wix.com/en/article/velo-tutorial-payment-provider-custom-extension-beta#http-endpoints) for this purpose. Provide the URL to the payment provider as part of the request to create the refund. Use the [submitEvent()](https://dev.wix.com/docs/velo/apis/wix-payment-provider-backend/submit-event.md) function in the logic for your endpoint to mark the refund as completed. Once this happens, the order associated with the refund is marked as refunded on you site's dashboard. ### HTTP Endpoints When you create a transaction or refund with your payment provider, you need to supply a URL for an HTTP endpoint that the provider can use to send your site updates for the statuses of transactions and refunds. To [expose](https://support.wix.com/en/article/velo-exposing-a-site-api-with-http-functions) these endpoints on your site, use the [Wix HTTP Functions API](https://www.wix.com/velo/reference/wix-http-functions). In the code for these endpoints, use the [submitEvent()](https://dev.wix.com/docs/velo/apis/wix-payment-provider-backend/submit-event.md) function to update the statuses of transactions and refunds on your site. Example endpoint code. This code must be saved in a backend file called `http-functions.js`: ```javascript import wixPaymentProviderBackend from 'wix-payment-provider-backend'; // An endpoint for receiving updates about transactions. export async function post_updateTransaction(request) { const transactionRequestBody = await request.body.json() const response = { 'headers': { 'Content-Type': 'application/json' } }; // Validate the request content. if (transactionRequestBody.state === 'Payment_Received') { // Update the transaction status on your site. This code assumes that the Wix // transaction ID and the payment provider's transaction ID are included in // the URL as query parameters. await wixPaymentProviderBackend.submitEvent({ 'event': { 'transaction': { 'wixTransactionId': request.query['wixTransactionId'], 'pluginTransactionId': request.query['pluginTransactionId'] } } }) return ok(response); } else { return badRequest(response); } } // An endpoint for receiving updates about refunds. export async function post_updateRefund(request) { const refundRequestBody = await request.body.json() const response = { 'headers': { 'Content-Type': 'application/json' } }; // Validate the request content. if (refundRequestBody.state === 'approved') { // Update the refund status on your site. This code assumes that Wix // transaction and refund IDs as well as other refund information are // included in the URL as query parameters. await wixPaymentProviderBackend.submitEvent({ 'event': { 'refund': { 'wixTransactionId': request.query['wixTransactionId'], 'wixRefundId': request.query['wixRefundId'], 'pluginRefundId': refundRequestBody.reference_id, 'amount': request.query['amount'] } } }) return ok(response); } else { return badRequest(response); } } ``` ### Add files to the plugin If you don't want to keep all of your code in the main plugin files, you can add files to the plugin's folder and import functions and objects into the main files. 1. Hover over the plugin folder's name and click **More Actions** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/d7a7ee13-5c33-4de8-bba0-d8a883585472/f793ba37-bd42-4377-bd93-b3936e7215c5.png) . 2. Select **New .js file**. 3. To import from these files to the main plugin files, use the following syntax: ``` import { functionName } from './myFileName.js'; ``` ### Test the plugin You can test your plugin before publishing your site using [functional testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) like you would with any backend Velo code. Make sure your `createAccount()`, `createTransaction()`, and `refundTransaction()` functions' return values are properly formatted. To test your plugin after deploying, add console logs to your code. The results appear in [Wix Logs](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 3: Deploy the plugin Once your code files are ready, you need to deploy your plugin and enable it on your site's dashboard. 1. Publish your site. 2. Go to the [Accept Payments](https://www.wix.com/my-account/site-selector/?buttonText=Set%20Up%20Payments&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7b%7bmetaSiteId%7d%7d/payments/) settings on your site's dashboard. 3. Click **See More Payment Options**. 4. Find the payment provider from your service plugin in the list and click **Connect**. 6. Enter the account credential information for the payment provider and click **Connect**. 8. Once your service plugin is deployed, the new payment provider appears on your site's **Checkout** page. ## Example Integration The payment provider service plugin allows you to implement a variety of different transaction flows. We created a [sample site](https://editor.wix.com/html/editor/web/renderer/new?siteId=3f28d8a0-f627-43b0-b053-b48a56f9f433&metaSiteId=01fab04f-60a2-46fa-b821-6304b12dc6f1) that demonstrates a basic transaction and refund integration with [Tazapay](https://tazapay.com/). > > **Note:** > Clicking the link to the sample site opens a copy of the site. Publishing the copy adds it to your Wix account. In order to get your copy of the sample site working, you need to create and save a Tazapay API key, and make some small changes to the site's code. ### Create and save a Tazapay API key Do the following to generate a Tazapay API key and use it to connect Tazapay as a payment provider on your copy of the sample site: 1. Publish a copy of the template site to save it to your Wix account. 2. Sign up for a free Tazapay sandbox account and log in. 3. On your Tazapay dashboard, click **API keys**. An API key and API secret are generated. Copy these values. 5. Open the [Tazapay API Reference](https://docs.tazapay.com/reference/create-checkout). 6. Under **Authentication**, enter your API key for **username** and your API secret for **password**. Your API authorization token, starting with `Basic`, is displayed in the sample request panel. Copy the token. 8. Open the [Accept Payments](https://www.wix.com/my-account/site-selector/?buttonText=Set%20Up%20Payments&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7b%7bmetaSiteId%7d%7d/payments/) page for your copy of the sample site. 9. Click **See More Payment Options**. 10. Find the Tazapay payment provider and click **Connect**. 11. Enter your API authorization token in the **API Auth Token** field. 12. Click **Connect**. ### Code setup Make the following 2 changes to your sample site's code to get it working: 1. On the Code sidebar, click **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/) . 2. Under **Service Plugins**, select **payment-provider** > **Tazapay** > **Tazapay.js**. 3. In the `createCheckoutSession` function, change the value of `callbackUrl` to reflect the details of your site. Change `` to your Wix account name and `` to the name of your copy of the sample site. ![](https://user-images.githubusercontent.com/89579857/207433821-3db59881-bdaa-46f1-ad40-0cfed500ae22.png) 4. Make the same changes to the `callbackUrl` value in the `createRefund` function. 5. Publish your site. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Form Submission Service Plugin ## Article: Tutorial: Form Submission Custom Extension ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-forms/tutorial-form-submission-service-plugin.md ## Article Content: # Tutorial: Form Submission Service Plugin Wix Service Plugins (formerly SPIs and custom extensions) allow you to implement custom logic to change how your site behaves and displays using Velo SPIs. For example, when you set up a form from the Wix Form app, there are a small number of validations to choose from. The form submission service plugin allows you to implement your own submission validations and options using code. You can also connect your site to form validation providers not currently supported by Wix. Any violations to these validations can be displayed on the form. You can manage service plugins from your site's dashboard, and they behave just like the ones Wix already supports. Learn more about using [service plugins](https://support.wix.com/en/article/velo-custom-app-extensions-using-spis). With the Form Submission service plugin you can define the submission validations for a form that fit your site's needs. Possible validations include: - Field validations: Check if the name contains only alphabetic characters. - Prevent duplicate submissions: Check for duplicate submissions based on certain criteria and prevent site visitors from submitting the same form multiple times. - Verify fields: Call a local post API call to check if the entered postal code is local. ## About this tutorial This tutorial explains how to set up and implement a form submission plugin on your site using Velo. We demonstrate how to validate your form submission by checking whether a site visitor entered a name correctly in alphabetical characters only. The process has 3 steps: 1. Create a new form submission validation plugin on our site. 1. Implement our plugin with custom code. 1. Deploy the plugin. ## Step 1: Create a new form submission plugin The first step in setting up your new plugin is to add it to your site. This process creates a new folder in the **Service Plugins** section of the Velo sidebar, which contains the files for your code. 1. [Add the Wix Forms app](https://www.wix.com/app-market/wix-forms) to your site. 1. Enable coding. If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. 1. Go to the **Service Plugins** section by clicking **Public & Backend**. 1. Hover over **Service Plugins** and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11fda387-da5a-430a-ada3-46def885b67f/2022/03/02/65d645bd-dee9-40dc-9f91-c44b06d72492/289e91de-47c5-429c-b61d-67bd8d4fd1ec.png). Then click **Form Submission**. 1. Follow the prompts to add the plugin and accept any terms and conditions that display. 1. Enter a name for your integration and click **Add & Edit Code**. The name can't contain spaces or special characters. 1. **Publish** your site. ![Extension](https://wixmp-833713b177cebf373f611808.wixmp.com/images/wix-forms-tutorial-submission-custom-extension-md_velo-articles_wix-forms-with-velo_images_service-plugin.png) ## Step 2: Implement the plugin The procedure in the previous step creates a folder in the service plugins section of the Velo sidebar. The name of the folder is based on the plugin you chose. Inside this is another folder with the name of the plugin you set up. This folder contains 2 files, **your-plugin-config.js** and **your-plugin.js**. In our case the files are named **submission-config.js** and **submission.js**. We implement the custom code for our plugin in these files. ## submission-config.js This file is where we write the code for setting up the plugin's configuration. The code in this file defines a function named `getConfig()` that returns an object containing the values used to display the form submission validations on your site. There are several apps that use forms. To validate form submission for a specific Wix app, we need to configure the file by defining the relevant `namespace` field in the plugin. In our example, we want to validate a form in the Wix Forms app, so we set the `namespace` value as `wix.form_app.form` in the service plugin's configuration file and enable the validation. ```js import * as formsSubmissionsExtensionProvider from 'interfaces-forms-v4-submission-extension'; export function getConfig() { return { namespaceConfigs: [ { namespace: "wix.form_app.form", submissionValidationEnabled: true } ] }; }; ``` ## submission.js This file is where we write the code for validating the first name form field. A site visitor tries to enter a first name with a non-alphabetical character. If there are any violations to the submission validation, a warning message can appear on the form as a text box. The code in this file defines a function named `validateSubmission()`. This function is called by Wix Forms to retrieve any violations to the validations provided by our plugin. The function accepts the following parameter: `options`: An object containing data about the source of the request, and the form submission information to validate. This information must include form ID. For more details, see the [Service Plugin Reference](https://www.wix.com/velo/reference/spis/wix-ecom/ecom-validations). The `validateSubmission()` function returns an array of validation objects (any validation violations in a site visitor's form). These validation violations can be displayed in the site visitor's form as text boxes with warnings. If there are no validation violations, the response is an object containing an empty list. ## The code: ```js import * as formsSubmissionsExtensionProvider from 'interfaces-forms-v4-submission-extension'; const options = { "submission": { "formId": "e9e8c6ff-a771-401b-b46e-82f7c742ef3d"; }; }; export const validateSubmission = async (options, context) => { const firstNameValidation = options.submission.submissions["first_name_abae"]; const alphabeticalPattern = new RegExp("^[a-zA-Z]+$"); if (!firstNameValidation.match(alphabeticalPattern)) { return { errors: [{ errorPath: "first_name", customErrorMessage: "First name can only contain alphabetical characters!" }] }; } else { return {} }; }; ``` **Line 1**: First we import the Wix Forms submission provider module. **Line 3-7**: We need to define the form ID so that the code would know which form to validate. To get the form ID, call [`querySubmissionsByNamespace()`](https://www.wix.com/velo/reference/wix-forms-v2/submissions/querysubmissionsbynamespace). We don't need to define the form field values separately in the `submissions` object, as the values will be taken directly from the published site form. **Line 9**: Then we export the `validateSubmission()` function where we set the custom logic for our validations. **Lines 10**: We save the first name field value into the `firstNameValidation` variable. **Line 11**: Define the [Regex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions) pattern so that the field only accepts alphabetical characters. **Line 12**: Here we check if a first name field value matches the Regex pattern. **Lines 13-20**: Set the violation description that can be displayed when there is a violation to the set pattern. If the form field value doesn't match the pattern, you get an error, and also form submission is aborted. ## Optional: Add files to the plugin If you don't want to keep all of your code in the main files, you can add files to the plugin's folder and import functions and objects into the main files. To do this, create a new file in the plugin's folder. To import from these files to the main files, use the following syntax: ```js import { functionName } from './myFileName.js'; ``` ## Optional: Test the plugin You can test your plugin before publishing your site using [functional testing](https://support.wix.com/en/article/velo-functional-testing-in-the-backend) like you would with any backend Velo code. Make sure your `validateSubmission()` function's return values are properly formatted. You can test your plugin after deploying. To do this, add console logs to your code. The results appear in the [Site Events log](https://support.wix.com/en/article/velo-about-site-monitoring). ## Step 3: Deploy the plugin Once your code files are ready, publish your site. Navigate to your site, and fill in a form. Any violations to the validations you've customized are displayed on the form. 1. Once your code files are ready, click **Save**, and then **Publish** your site. 1. After your service plugins are published, open your site. 1. Your service plugin is then deployed, and any validation violations should appear on your form. If a site visitor tries to enter a first name with non-alphabetical characters, there will be an error message that could be shown on the site. The validation violation response looks like this: ```js { "errors": [ { "errorPath": "first_name_abae", "customErrorMessage": "First name can only contain alphabetical characters!" } ] } ``` ## Remove the plugin You can remove the plugin from your site. Do the following: 1. In the **Public & Backend** section of the Velo sidebar, under **Service Plugins**, hover over the plugin's folder and click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/c4ff84cb-4d96-4e1a-966a-9c6b66dca0b2/062297c2-c368-4aee-b705-f0a55abc5bf6.png) . 1. Click **Delete**. 1. Delete the plugin in the prompt. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Formatting and Layout When Coding ## Article: About Formatting and Layout When Coding ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/about-formatting-and-layout-when-coding.md ## Article Content: # About Formatting and Layout When Coding When you're developing your site, it's important to format the text and layout the page the way you want it. You can use code to dynamically [format your text elements](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/about-formatting-text-elements.md) and to correctly [format dates](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/formatting-dates.md) in text. It's also essential to understand how [page elements interact with each other as they change size](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/how-page-layout-is-affected-when-elements-change-size.md). This knowledge will help you create a responsive design and maintain control over how your site appears. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Formatting Text Elements ## Article: About Formatting Text Elements ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/about-formatting-text-elements.md ## Article Content: # About Formatting Text Elements Formatting [text elements](https://dev.wix.com/docs/velo/api-reference/$w/text/introduction.md) is important for making your site's text content visually appealing and consistent. You can edit text elements directly in the editor using the **Edit Text** button. However, sometimes you might want to change their formatting or content dynamically through code. This method is especially useful for adjusting text based on user interactions or when working with data-driven content. For example, you might want to: - Change the formatting or content based on user interaction or other events. - Apply [data from a dataset to page elements](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/calculating-and-displaying-collection-data.md#text-elements) dynamically. When you edit the content of a text element using the [`text`](#the-text-property) or [`html`](#the-html-property) property, it will affect its formatting. > **Note:** You can also format text elements using the [text CSS classes](https://dev.wix.com/docs/velo/api-reference/$w/text/css-classes.md). ## Supported IDEs - The editor (Wix Studio and Wix Editor) - The Wix IDE (Wix Studio) - Git Integration (Wix Studio and Wix Editor) ## The `text` property When you change a text element's content using it's [`text`](https://dev.wix.com/docs/velo/api-reference/$w/text/text.md) property, it keeps the existing formatting. The formatting in the text element will be uniform across all its content, meaning different parts of the text cannot have different formats. If you applied more than one format in the editor, the first style will be used for all the content set with the text property. ## The `html` property When you change a text element's content using it's [`html`](https://dev.wix.com/docs/velo/api-reference/$w/text/html.md) property, its formatting is set by the markup you apply. You can format the content using supported [HTML formatting](https://dev.wix.com/docs/velo/api-reference/$w/text/introduction.md#api-reference_$w_text_formatting-text). ### Themes When using [heading or paragraph tags](https://dev.wix.com/docs/velo/api-reference/$w/text/introduction.md#api-reference_$w_text_text-theme-translation), the text inside the tags is formatted according to the [site's themes](https://support.wix.com/en/article/studio-editor-about-site-styles). > **Note:** Themes are defined site-wide, so when you update the formatting of a theme, all relevant text formatting will update as well. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Formatting Dates ## Article: Formatting Dates ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/formatting-dates.md ## Article Content: # Format Dates in Text Elements with Code Formatting dates within [text elements](https://dev.wix.com/docs/velo/api-reference/$w/text/introduction.md) using code allows you to display dates dynamically and tailor them to different locales. Use JavaScript's `Date` class' [`toLocaleDateString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString) method to format dates on your site. ## Format dates on your site 1. To render a date in the site visitor's locale settings on their computer, call `toLocaleDateString()` on your date object without specifying any parameters. For example: ```js $w("currentDate").text = new Date().toLocaleDateString(); // Examples: // en-US - 1/30/2025 // en-UK - 30/01/2025 ``` 1. Add [parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString#parameters) to customize the date's locale. For example: ```js const options = { day: "numeric", month: "short", year: "numeric", }; $w("currentDate").text = new Date().toLocaleDateString("en-US", options); // Jan 30, 2025 ``` ## Format dates from a dataset on your site If you're displaying dates retrieved from a dataset, you can format all dates retrieved from your dataset in the same way using the [dataset](https://dev.wix.com/docs/velo/api-reference/$w/dataset/introduction.md) element's [`onReady()`](https://dev.wix.com/docs/velo/api-reference/$w/dataset/on-ready.md) and [`getCurrentItem()`](https://dev.wix.com/docs/velo/api-reference/$w/dataset/get-current-item.md) with `toLocaleDateString()`. For example: ```js $w("#myDataset").onReady(() => { const date = $w("#myDataset").getCurrentItem().dateField; // The dateText is linked to the dataset and is initially hidden. $w("#dateText").text = new Date(date).toLocaleDateString(); $w("#dateText").show(); }); ``` > **Note:** You can also [format dates from a dataset](https://support.wix.com/en/article/cms-formatting-live-site-date-and-time-content-from-your-collection) in the editor without code. ## See also - [About formatting text elements](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/about-formatting-text-elements.md) - [Text element](https://dev.wix.com/docs/velo/api-reference/$w/text/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: How Page Layout is Affected When Elements Change Size ## Article: How Page Layout Is Affected When Elements Change Size ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/how-page-layout-is-affected-when-elements-change-size.md ## Article Content: # Effect of Resizing Elements on Page Layout
Important: This article is only relevant for the Wix Editor. The way elements interact with each other in Wix Studio depends on the properties and structure you implement when building your site. Learn more about Wix Studio page structure.
When you add code to your site, the height of your page elements can change due to expanding, collapsing, or dynamically resizing elements. These changes can affect your page layout and the overall height of the page. This article explains the rules that determine when and how changes in the height of page elements affect your layout, helping you control how your page looks in different situations. Note that these rules only apply to elements that are above/below one another on the page.
Tip: Use a container box to group elements that you want to react together. Set the container box to transparent if you don't want it to be visible on the page. Learn more about container boxes.
## Why elements shift on a page Elements on page can shift due to other elements: - [Collapsing or expanding](#collapsing-and-expanding-elements). - [Growing](#growing-elements) to accommodate more content. Elements on a page don't shift due to animations, or other elements being hidden or shown. In this context, a page footer acts like an element if an element above it collapses, expands, or grows.
Tips:
  • To remove an unwanted gap between your footer and the last element on the page, double-click the footer handle.
  • If you can't see elements at the bottom of the page and they aren't hidden, double-click the footer handle to increase the page size to include all the elements.
## Collapsing and expanding elements When an element collapses, the elements below it can either move up to take its position or remain where they are. This behavior depends on the following factors: - **The size of the gap between elements.**\ If the gap is 70 pixels or less, the gap disappears, causing the element below the collapsed element to shift up. If the gap is greater than 70 pixels, the elements below stay in place. Similarly, when an element expands, the elements below it move down if the gap is 70 pixels or less; otherwise, they stay where they are.
Calculating the gap between elements The gap is the distance from the bottom of the higher element to the top of the lower element. In order to calculate the gap, you must know the exact size and position of each element. You can see this information in the Wix Editor Toolbar.

To calculate the gap:
  1. Find the bottom edge of the higher element by adding its Y value to its height. In the example below, the blue element is positioned at 150 pixels and has a height of 50 pixels, so its bottom edge would be at 200 pixels (150 + 50). 
  2. Calculate the gap by subtracting the bottom edge of the higher element from the Y value of the lower element. In the example below, the purple element is at 250 pixels, so the gap is 50 pixels (250 - 200).

We use the same process to find that, in the example below, the gap between the purple and green elements is 100 pixels.
- **Whether there is an [anchor](https://support.wix.com/en/article/wix-editor-about-anchors-9563) directly above the collapsed elements.**\ If an element directly below an anchor collapses, the elements below don't move up to take its place. ### Knock-on effect When an element collapses and the element below it has a gap of less than 70 pixels, it moves up. Then, if the gap between this element and the one below is greater than 70 pixels, the one below stays in place. Otherwise, it moves up to maintain its gap from the element above. Similarly, when an element expands and the element below it moves down, if the gap between this element and the one below is greater than 70 pixels, the one below stays in place. Otherwise, it moves down to maintain its gap from the element above.
Example: A 50px gap and a 100px gap In the image above, the gap between the blue and purple elements is 50 pixels. Since this gap is less than 70 pixels, when the blue element collapses, the purple element moves up. However, the gap between the purple and green elements is 100 pixels. Since this gap is greater than 70 pixels, when the blue element collapses, the purple element moves up, but the green element does not move because of the larger gap.
Example: A 50px gap and another 50px gap In this example, both gaps are 50 pixels. Consequently, when the blue element collapses, both the purple and green elements move up into the space previously occupied by the blue and purple elements, respectively.
## Growing elements The height of an element can increase when its content increases. For example, elements connected to a dataset may need to expand to display additional information. When an element grows, the elements below may move down to accommodate the extra space: - **Gaps that are 70 pixels or less are maintained.**\ Elements 70 pixels or less below the growing element maintain the size of the gap. For example, if the gap started at 50 pixels, it stays at 50 pixels, regardless of how much the higher element grows. - **Gaps that are more than 70 pixels shrink until they reach 10 pixels.**\ If the gap is initially greater than 70 pixels, the elements below the growing element do not move, unless the gap would shrink to less than 10 pixels. In that case, the element moves down to maintain a 10 pixel gap. ### Knock-on effect When an element grows and the element below it moves down as a result, and the gap between this element and the one below is greater than 70 pixels, it doesn't move unless that gap falls to less than 10 pixels, in which case it maintains a 10 pixel gap. If the gap is 70 pixels or less, the gap is maintained. ## Vertical overlap The rules in this article do not apply to elements positioned next to each other. However, if the lower element overlaps vertically by more than half, meaning its top edge is higher than the vertical middle of the higher element, it does not affect the elements below it when it collapses. For example:
On the left side, the purple element does not overlap vertically with the blue element. Therefore, because the gap between the purple and green elements is less than 70 pixels, when the purple element collapses, the green element moves up. However, on the right side, the purple element overlaps vertically with the blue element. Therefore, regardless of the size of the gap between the purple and green elements, when the purple element collapses, the green element is unaffected. ## Container boxes A [container box](https://dev.wix.com/docs/velo/api-reference/$w/box/introduction.md) is an element that contains other elements. Elements attached to the same container box affect each other, as do different container boxes. Elements attached to different container boxes do not affect each other. ## Grouped elements When elements are [grouped together](https://support.wix.com/en/article/wix-editor-grouping-and-ungrouping-elements), they behave as a single element. This means that if an element above the group collapses, expands, or grows, all elements in the group move together. Similarly, the rules apply if the group itself collapses, expands, or grows. The top of the group is equal to the top of the highest element in the group, while the bottom, right, and left of the group are calculated from the lowest, right-most, and left-most elements, respectively. These values are used to determine gap sizes and vertical overlap. ## See also - [Wix Editor: Working with elements](https://support.wix.com/en/article/wix-editor-working-with-elements) - [Using your Editor toolbar](https://support.wix.com/en/article/using-your-editor-toolbar) - [Container box](https://dev.wix.com/docs/velo/api-reference/$w/box/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Repeater Item Templates ## Article: About Repeater Item Templates ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/about-repeater-item-templates.md ## Article Content: # Velo Repeaters: About Repeater Item Templates A repeater has an item template that contains the elements and initial data that are used when new items are created. The template's initial state is the state of the first repeated item that appears in the Editor. When new repeated items are created, the values of the repeated elements are set to the values from the item template by default. You can then override those default values by connecting the repeated items to a dataset or by using the `onItemReady()` event handler that runs when new items are created. Using code, you can set the properties of, get the properties of, or call functions on the elements of the item template by selecting the elements from the [global scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md) with the `$w()` function. ### Example To demonstrate how the item template affects your items and how you can change the item template with code, we will use the following simplified example.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/dc996ffc-debf-4afc-a4e5-409206ad2e00.png)
The example has the following page elements: - Two buttons: - The **Add next item** button adds an item to the repeater from a static array of data. - The **Change content globally** button changes the repeater's item template by setting a value in one of its element's properties from a [global scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md) selector. - A repeater where each repeated item in the repeater contains two text elements: - The text element on the left shows the item's ID. - The text element on the right shows the item's content. The example has the code shown below that: - Defines the static data that will be used in the repeater. - Defines an `onReady()` event handler that: - Sets the initial data of the repeater to be an empty array. - Adds the event handlers that make the buttons work. - Adds a repeater `onItemReady()` event handler, which runs when new items are created. It sets the `text` values of the text elements to values from the new item's corresponding data. If there is no `content` value, the new item will have the default value given to it by the repeater's item template. ```javascript const exampleData = [ { _id: "1", content: "First item" }, { _id: "2" }, { _id: "3", content: "Third item" }, { _id: "4" }, ]; let added = 0; $w.onReady(function () { $w("#myRepeater").data = []; $w("#addNext").onClick((event) => { $w("#myRepeater").data = exampleData.slice(0, ++added); }); $w("#changeGlobally").onClick((event) => { $w("#repeatedText").text = "New template text"; }); $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#repeatedId").text = itemData._id; if (itemData.content) { $item("#repeatedText").text = itemData.content; } }); }); ``` ### Scenario We will now run through a simple scenario to demonstrate how the item template is used. --- When the page first loads, the `onReady()` function runs, setting all the event handlers mentioned above and clearing out the repeater's data. That means, when the page loads, you won't see the repeater at all. Remember, the initial values for the item template are defined in the Editor. That means the text value of the `repeatedText` element in the item template is now `Original template text`. --- If we then click the **Add next item button**, the first element from the data array (`{"_id": "1", "content": "First item"},`) is added to the repeater. Since the first element contains values for the `_id` and content properties, the `onItemReady()` event handler will assign these values to the repeated elements of the new item, overwriting the default values from the item template. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/28/6318e227-9858-4f98-8fb0-99680c5e7f56.png)
And the `text` value of the `repeatedText` element in the item template is `Original template text`. --- If we then click the **Add next item** button again, the second element from the data array (`{"_id": "2"}`) is added to the repeater. Since the second element does not contain the `content` property, the `onItemReady()` event handler does not assign a value to the `text` property of the new item's `repeatedText` element. So the default `text` value from the item template is not overridden. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/28/58081870-31a8-4561-a42f-202a645e1d98.png)
And the `text` value of the `repeatedText` element in the item template is still `Original template text`. --- If we then click the **Change content globally** button, which selects the `repeatedText` element using a [global scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md) selector, the `text` value of the `repeatedText` elements in the current repeated items will change. Because the `repeatedText` element was selected with a [global scope](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md) selector, changing its `text` value also changes the value in the item template. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/28/1470a6d1-8697-46b0-b791-9188022a0711.png)
And the `text` value of the `repeatedText` element in the item template is now `New template text`. --- If we then click the `Add next item` button, the third element from the data array (`{"_id": "3", "content": "Third item"}`) is added to the repeater. Since the third element contains values for the `_id` and `content` properties, the `onItemReady()` function will assign these values to the repeated elements of the new item, overwriting the default values from the item template. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/28/fac61b81-0666-49f8-9f5d-58d316301f42.png)
And the `text` value of the `repeatedText` element in the item template is still `New template text`. --- Finally, if we click the **Add next item** button again, the fourth element from the data array (`{"_id": "4"}`) is added to the repeater. Since the fourth element does not contain the `content` property, the `onItemReady()` event handler does not assign a value to the `text` property of the new item's `repeatedText` element. So the default `text` value from the item template is not overridden. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/28/e24e315b-2918-4915-89c1-a84e9c838308.png)
And the `text` value of the `repeatedText` element in the item template is still `New template text`.
**Tip:** Adding items to the repeater might [affect your page layout](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/how-page-layout-is-affected-when-elements-change-size.md).
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Understanding the Scope of Selector Functions ## Article: Understanding the Scope of Selector Functions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md ## Article Content: # Velo Repeaters: Understanding the Scope of Selector Functions
Visit the Velo by Wix website to onboard and continue learning.
A **selector** function is used to select specific page elements so you can work with them in code. A **scope** defines which elements you can select and how they behave when they are selected. Depending on which **selector** you use, you will be able to select elements from the different **scopes** described below and the selected elements will behave accordingly. There are two types of selector functions: * Global Scope Selectors * Repeated Item Scope Selectors ### Global Scope The `$w()` function that is available to your **Page Code** and **masterPage.js** files selects elements in the global scope. A selector with global scope can be used to select any element that is not contained in a repeater. You can also use it to select an element that is contained in a repeater, but it is important to understand what that selection means. * * * When you select an element contained in a repeater from the global scope, and you get the value of one of the element's properties, you receive the value of that element's property from the repeater's [item template](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/about-repeater-item-templates.md). For example, here `templateText` will be the `text` value of the `myRepeatedText` element from the repeater's item template. $w.onReady( function () { let templateText = $w("#myRepeatedText").text; } ); * * * When you select an element contained in a repeater from the global scope and you set the value of one of the element's properties or call one of the element's functions, the value is set or the function is called on the repeater's [item template](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/about-repeater-item-templates.md) **and** all repeated instances of that element. For example, here the item template will change so that `"New Text"` will be the `text` value of the `myRepeatedText` element. Also, all existing repeated items will have the `text` value of their `myRepeatedText` element set to `"New Text"`. $w.onReady( function () { $w("#myRepeatedText").text = "New Text"; } ); And here the item template will change so that the `myRepeatedImage` element is hidden. Also, all existing repeated items will have their `myRepeatedImage` element hidden. $w.onReady( function () { $w("#myRepeatedImage").hide(); } ); ### Repeated Item Scope There are two instances where you get a repeated item scope selector: * The `$item` parameter of the [`forEachItem()`](https://www.wix.com/velo/reference/$w.Repeater.html#forEachItem), [`forItems()`](https://www.wix.com/velo/reference/$w.Repeater.html#forItems), and [`onItemReady()`](https://www.wix.com/velo/reference/$w.Repeater.html#onItemReady) event handlers. * When calling the [`$w.at()`](https://www.wix.com/velo/reference/$w.html#at) function and passing it an `event` whose `context` is `"COMPONENT_SCOPE"`. This is usually done in an event handler that handles events on an element inside a repeater. A selector with repeated item scope selects the instance of a repeating element in the current repeating item and its descendants. You cannot change the item template using a selector with repeated item scope. For example, here when the `myRepeatedImage` element is clicked, the `text` value of the `myRepeatedText` element from the repeated item where the image was clicked will be changed to `"Selected"`. All the other `myRepeatedText` elements in the other items of the repeater will not be affected. $w.onReady( function () { $w("#myRepeatedImage").onClick( (event) => { let $item = $w.at(event.context); $item("#myRepeatedText").text = "Selected"; } ); } ); ### Example To demonstrate the different selector scopes and what selecting from those scopes means, we will use the following example.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/26/c5c5418d-6d9d-4d2f-b2a3-f2cedc73510f.png)
Here we have a repeater that contains a number of items set in the editor. Each item can be either "selected" or "unselected". The selected status of each item is indicated by that item's **itemSelected** text element. The number of selected items in the repeater is indicated by the page's **numSelected** text element.  There are several ways a user can select elements: * Click the **selectAll** button to select all the items. * Click the **selectLeft** or the **selectRight** buttons to select all the items in the left or right column. * Click a **repeatedImage** instance to select the item with that image. Here is the code: ```javascript $w.onReady( () => { $w("#selectAll").onClick( (event) => { $w("#itemSelected").text = "Selected"; $w("#numSelected").text = $w("#myRepeater").data.length.toString(); } ); $w("#repeatedImage").onClick( (event) => { let $item = $w.at(event.context); if($item("#itemSelected").text === "Unselected") { $item("#itemSelected").text = "Selected"; $w("#numSelected").text = (Number($w("#numSelected").text) + 1).toString(); } else { $item("#itemSelected").text = "Unselected"; $item("#numSelected").text = (Number($w("#numSelected").text) - 1).toString(); } } ); $w("#selectLeft").onClick( (event) => { selectColumn(0, 2); } ); $w("#selectRight").onClick( (event) => { selectColumn(1, 2); } ); } ); function selectColumn(columnIndex, numColumns) { $w("#myRepeater").forEachItem( ($item, itemData, index) => { if(index % numColumns === columnIndex && $item("#itemSelected").text === "Unselected") { $item("#itemSelected").text = "Selected"; $item("#numSelected").text = (Number($w("#numSelected").text) + 1).toString(); } } ); } ``` Let's analyze this code one section at a time. * * * First, let's look at the `onClick()` event handler for the `selectAll` button. ```javascript $w("#selectAll").onClick( (event) => { $w("#itemSelected").text = "Selected"; $w("#numSelected").text = $w("#myRepeater").data.length.toString(); } ); ``` All selectors in this snippet are global selectors. That means that the selection of the `itemSelected` text element on line 2 is from the global scope. So when its `text` value is set, the `text` value of the `itemSelected` element in the repeater's item template and the `text` values of all the repeated `itemSelected` elements in all of the repeated items are set to `"Selected"`. This is what allows us to "select" all the items in one line of code. (We have also changed the item template. But since we will not be creating any new items, the change is of no consequence.) * * * Next, let's look at the `onClick()` event handler for the `repeatedImage` element. ```javascript $w("#repeatedImage").onClick( (event) => { let $item = $w.at(event.context); if($item("#itemSelected").text === "Unselected") { $item("#itemSelected").text = "Selected"; $w("#numSelected").text = (Number($w("#numSelected").text) + 1).toString(); } else { $item("#itemSelected").text = "Unselected"; $item("#numSelected").text = (Number($w("#numSelected").text) - 1).toString(); } } ); ``` On line 2 we create a repeated item scope selector. That means all the selections using that selector in the body of the function are repeated item scope selections. So when we select a repeated element, such as the `itemSelected` text element, we are selecting the individual instance of that element that is in the same repeated item as the image that was clicked. All the other `itemSelected` elements in the other items of the repeater will not be affected. Notice that on lines 5 and 9 we also use a global selector to select an element outside the repeater and change the value of one of its properties. * * * Finally, let's look at the `onClick()` event handler for the `selectLeft` and `selectRight` buttons. ```javascript $w("#selectLeft").onClick( (event) => { selectColumn(0, 2); } ); $w("#selectRight").onClick( (event) => { selectColumn(1, 2); } ); ``` Both of these event handlers call the `selectColumn()` function which "selects" all of the items in a given column. ```javascript function selectColumn(columnIndex, numColumns) { $w("#myRepeater").forEachItem( ($item, itemData, index) => { if(index % numColumns === columnIndex && $item("#itemSelected").text === "Unselected") { $item("#itemSelected").text = "Selected"; $item("#numSelected").text = (Number($w("#numSelected").text) + 1).toString(); } } ); } ``` The `$item` parameter of the `forEachItem()` function is a repeated item scope selector. That means all the selections using that selector in the body of the function are repeated item scope selections. So when we select the `itemSelected` text element, which is a repeated element, we are selecting the individual instance of that element for the specific item of the current iteration of the `forEach` loop. All the other `itemSelected` elements in the other items of the repeater will not be affected. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: The Lifecycle of Repeated Items ## Article: The Lifecycle of Repeated Items ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/the-lifecycle-of-repeated-items.md ## Article Content: # Velo Repeaters: The Lifecycle of Repeated Items When working with a repeater using code, it is important to understand how the items in the repeater are created, updated, and deleted. But first, you need to understand the relationship in a repeater between its repeated items and its data.  A repeater's data is stored as an array of objects. You get and set that array using the repeater's `data` property. Each object in the array must contain a unique `_id` property which is used to match the object's data to the individual repeated items of the repeater as described below. The value of the `_id` property can only be comprised of alphanumeric characters and hyphens (`-`). Other than `_id`, the objects in the repeater's data array can contain anything you want. For example, a simple array of repeater data may look like this: ```javascript [ { "_id": "1", "firstName": "John", "lastName": "Doe", "image": "http://someImageUrl/john.jpg" }, { "_id": "2", "firstName": "Jane", "lastName": "Doe", "image": "http://someImageUrl/jane.jpg" } ] ``` Repeater data is not automatically applied to the elements in the repeated items. You choose how to use the repeater's data in the `onItemReady()`, `onItemRemoved()`, `forItems()`, and `forEachItem()` callback functions. Most often, you will be applying the data of a repeated item to the properties and functions of the repeated elements contained in that repeated item. For more information on these functions, see the [API Reference](https://www.wix.com/velo/reference/$w.Repeater.html). ### Creating New Items When you set a repeater's `data` property, new repeated items are created for each object that has an `_id` value that is not already present in the current array of data objects. The elements in the new items are first populated with the data of the repeater's item template. Then the `onItemReady()` event handler is triggered for each of the new items. Usually, you will overwrite some or all of the data populated from the item template in the `onItemReady()` event handler with the data for that specific item. When all of the `onItemReady()` event handlers have finished running, the new items are displayed. For example, if the repeater's `data` property currently contains the following array: [{"_id": "1", "content": "First"}] And then you set its value to this array: [{"_id": "1", "content": "First"},{"_id": "2", "content": "Second"}] One new repeated item is created because an item with ID `"2"` did not yet exist. The `onItemReady()` event handler will run once for the new item. There you can set up its contents. ### Updating Existing Items When you set a repeater's data property, nothing occurs to repeated items whose IDs were already in the array of data objects, even if other data in the object has changed. To update repeated items with the new data, use the `forEachItem()` or `forItems()` functions. For example, if the repeater's `data` property currently contains the following array: [{"_id": "1", "content": "First"}] And then you set its value to this array: [{"_id": "1", "content": "New First"}] The content of elements in the existing repeated item will not change because an item with ID `"1"` already exists. To update the contents you need to call either the `forEachItem()` or `forItems()` functions. ### Removing Items When you set a repeater's `data` property, repeated items are removed if their IDs are no longer in the array of data objects. The `onItemRemoved()` event handler is triggered for each of the removed items. For example, if the repeater's `data` property currently contains the following array: [{"_id": "1", "content": "First"},{"_id": "2", "content": "Second"}] And then you set its value to this array: [{"_id": "1", "content": "First"}] One repeated item is removed because an item with ID `"2"` no longer exists. The `onItemRemoved()` event handler will run once for the removed item. ### Example To demonstrate the full lifecycle of repeated items, we will use the following simplified example. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/1c9e7e93-ef4c-44aa-a495-ae82d5c10a91.png)
The example has the following page elements: * Three buttons: * The **Add** button adds more items to the repeater. * The **Remove** button removes the added items from the repeater. * The **Update** button updates the first item in the repeater. * A repeater where each repeated item in the repeater contains two text elements: * The text element on the left shows the item's ID. * The text element on the right shows the item's content. The example has the code shown below that: * Defines two static arrays of data that we will use for the repeater. * An `onReady()` function that: * Sets the initial data of the repeater. * Adds the event handlers that make the buttons work. * Adds the repeater `onItemReady()` and `onItemRemoved()` event handlers that run when new items are created and removed. ```javascript const sampleData1 = [ {"_id": "1", "content": "First item"}, {"_id": "2", "content": "Second item"} ]; const sampleData2 = [ {"_id": "3", "content": "Third item"}, {"_id": "4", "content": "Fourth item"} ]; $w.onReady(function () { $w("#myRepeater").data = sampleData1; $w("#addButton").onClick( (event, $w) => { let repeaterData = $w("#myRepeater").data; $w("#myRepeater").data = repeaterData.concat(sampleData2); } ); $w("#removeButton").onClick( (event, $w) => { $w("#myRepeater").data = sampleData1; } ); $w("#updateButton").onClick( (event, $w) => { let repeaterData = $w("#myRepeater").data; repeaterData[0].content = "Changed item"; $w("#myRepeater").data = repeaterData; $w("#myRepeater").forItems( ["1"], ($w, itemData, index) => { $w("#repeatedId").text = itemData._id; $w("#repeatedText").text = itemData.content; } ); } ); $w("#myRepeater").onItemReady( ($w, itemData, index) => { $w("#repeatedId").text = itemData._id; $w("#repeatedText").text = itemData.content; } ); $w("#myRepeater").onItemRemoved( (itemData) => { console.log(`Removed: ${itemData.content}`); } ); } ); ``` #### Scenario We will now run through a simple scenario to demonstrate the complete item lifecycle. * * * When the page first loads, the `onReady()` function runs, setting all the event handlers mentioned above and setting the value of the repeater's `data` property to be the first array of sample data. $w("#myRepeater").data = sampleData1; That means, when the page loads, the repeater's single item defined in the Editor will be replaced by two new items. The `onItemRemoved()` event handler does not run for the item that was removed because it was a static item defined in the Editor. However, the `onItemReady()` event handler runs for each of the new items that are added. In our case, we've defined the event handler to set the `text` values of the repeated item's elements from the corresponding data in the repeater's `data` array. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/96b71417-777e-41ea-bde7-0294d3d64887.png)
* * * If we then click the **Add** button, the repeater's `data` property is reset with a new array that contains the elements from both sample data arrays. let repeaterData = $w("#myRepeater").data; $w("#myRepeater").data = repeaterData.concat(sampleData2); Because two of the elements in the `data` array are new, two corresponding new items are added to the repeater and the `onItemReady()` event handler runs once for each of these new items. The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/5005b6a3-d619-4c88-a205-d563bd53a52f.png)
* * * If we then click the **Remove** button, the repeater's `data` property is reset back to the first array of sample data. $w("#myRepeater").data = sampleData1; Because two of the elements from the `data` array are removed, the two corresponding items are removed from the repeater and the `onItemRemoved()` event handler runs once for each of these removed items. $w("#myRepeater").onItemRemoved( (itemData) => { console.log(`Removed: ${itemData.content}`); } ); The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/ab0d010e-fdf6-4a57-98fa-462586d2ed71.png)
* * * Finally, if we click the **Update** button, the repeater's `data` property is reset with an array with the same items as before, but with one value changed. let repeaterData = $w("#myRepeater").data; repeaterData[0].content = "Changed item"; $w("#myRepeater").data = repeaterData; Because the IDs of the elements in the `data` array have not changed, nothing happens automatically. We need to manually call the `forItems()` function to update the elements in the corresponding repeated item. $w("#myRepeater").forItems( ["1"], ($w, itemData, index) => { $w("#repeatedId").text = itemData._id; $w("#repeatedText").text = itemData.content; } ); The repeater now looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/11/29/72fd4185-f76e-41df-9263-8d4a40d0cd50.png)
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using Connected Datasets and Repeaters ## Article: Using Connected Datasets and Repeaters ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/using-connected-datasets-and-repeaters.md ## Article Content: # Velo Repeaters: Using Connected Datasets and Repeaters
Visit the Velo by Wix website to onboard and continue learning.
Sometimes you will need to connect a repeater and its elements to two datasets, one of which is filtered by the other. If you then want to control the datasets with code, you need to understand how the datasets behave when selected from different [scopes](https://support.wix.com/en/article/understanding-the-scope-of-w-selectors). For example, suppose you have some collections related to music. One collection stores information about bands and another collection stores information about songs. You can create a repeater which has an item for each band. The band items could also list all the songs related to each band. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/12/26/08be89bb-a23b-4666-a750-9d5343d85023.png)
To create the repeater shown above, you need two datasets, one for the **Bands** collection and another for the **Songs** collection. The **Bands dataset** is connected to the repeater itself. That causes the repeater to create an item for each band in your collection. You can also connect some of the elements contained in the repeater's items to the **Bands dataset**. For example, you can display the band's name and picture in each item. The **Songs dataset** is also connected to some of the elements contained in the repeater's items. For example, you can display a list of songs by the band in an item using a table. The **Bands dataset** and **Songs dataset** need to be connected in some manner. The connection allows you to show the correct songs in each band's repeater item. ### Connecting Datasets There are two ways you can connect the two datasets. The way you choose depends on how your collections are set up.  If your Songs collection contains a reference field that refers to items in the Bands collection, you can set a filter on the Songs dataset as shown here:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/12/26/14670565-e34e-44ad-bd0c-b8bbc3450e97.png)
If your Songs collection contains a regular field that matches a field in the Bands collection, you can set a filter on the Songs dataset as shown here:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/12/26/dba369c9-59e8-4140-b410-df611ebfe630.png)
### Using the Connected Datasets with Code Before discussing each individual case, it is important to understand what is happening behind the scenes when you connect the datasets mentioned above to the elements in a repeater's items. Even though there is only one **Bands dataset** and one **Songs dataset**, we want those datasets to contain different items, depending on the context in which they are being used.  For example, above we connected a table which appears in each of the repeater's items to the **Songs dataset**. We don't want all of the songs in the **Songs** collection to appear in each repeater item. Instead, we only want to display the songs relevant to a particular item's band.  In order to make that work, a virtual **Songs dataset** is created for each repeater item. When you select the Songs dataset using a [repeated item scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting one of these virtual datasets. However, if you select the Songs dataset using a [global scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting the regular dataset.   We will now discuss each individual case of how to use the types of datasets mentioned above with repeaters: * Using the Filtered Dataset inside the Repeater * Using the Filtered Dataset outside the Repeater * Using the Unfiltered Dataset inside the Repeater * Using the Unfiltered Dataset outside the Repeater #### Using the Filtered Dataset inside the Repeater When you select a dataset that is filtered using one of the methods mentioned above from a [repeated item scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting a virtual dataset. That virtual dataset only contains the items that relate to the current item in the dataset it is connected to via a filter. It also only controls elements that are connected to it in the current repeater item. For example, in each repeater item we only see the songs that are related to that item's band. If we want to let users sort that list of songs, we could use the dataset `setSort()` function. In the repeater shown above, what looks like the table header is actually a group of three buttons. Each of those buttons has an `onClick` event handler that sorts the songs table based on a field. ```javascript import wixData from 'wix-data'; $w.onReady( () => { $w("titleSort").onClick( (event) => { let $item = $w.at(event.context); $item("#songsDataset").setSort(wixData.sort().ascending("title")); } ); $w("lengthSort").onClick( (event) => { let $item = $w.at(event.context); $item("#songsDataset").setSort(wixData.sort().ascending("length")); } ); $w("albumSort").onClick( (event) => { let $item = $w.at(event.context); $item("#songsDataset").setSort(wixData.sort().ascending("album")); } ); } ); ``` Since the Songs dataset is selected using a repeated item scope selector, the sort is applied to a virtual dataset. So when a user clicks a button to sort the songs in one of the items the songs in all the other items are not affected. Note that we don't have to write the sorting code for each virtual dataset. We write the code only once and it is applied to the correct item automatically.  #### Using the Filtered Dataset outside the Repeater When you select a dataset that is filtered using one of the methods above from a [global scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting a regular dataset. That regular dataset only controls the elements connected to it that are not contained in repeater items. The elements inside repeater items are not affected even though they are connected to the same dataset. For example, let's say we had a button outside the repeater shown above. If we called the dataset `setSort()` function in the button's `onClick` event handler nothing would happen to any of the songs tables in the repeater's items. If there was a table outside of the repeater connected to the dataset, its items would be sorted. #### Using the Unfiltered Dataset inside the Repeater When you select the dataset that the repeater itself is connected to using a [repeated item scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting a virtual dataset. That virtual dataset contains only one item, the one that the current repeater item is based on. For example, each item in the repeater shown above has its own virtual **Bands dataset**. That dataset contains only one band, the band in the current repeater item. #### Using the Unfiltered Dataset outside the Repeater When you select the dataset which the repeater itself is connected to using a [global scope selector](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/repeaters/understanding-the-scope-of-selector-functions.md), you are selecting a regular dataset. That regular dataset controls the number and order of the repeater items and any elements connected to it that are not contained in repeater items. The elements inside repeater items are not affected even though they are connected to the same dataset. For example, let's say we had a button outside the repeater shown above. We could use it to sort the items in the repeater by calling the dataset `setSort()` function in the button's `onClick` event handler. Nothing would happen to any of the elements inside the repeated items, but the items themselves would be sorted. ```javascript import wixData from 'wix-data'; $w.onReady( () => { $w("repeaterSort").onClick( (event) => { if(event.target.label === "A-Z"){ $w("#bandsDataset").setSort(wixData.sort().ascending("name")); event.target.label = "Z-A"; } else { $w("#bandsDataset").setSort(wixData.sort().descending("name")); event.target.label = "A-Z"; } } ); } ); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Multi-State Boxes ## Article: About Multi-State Boxes ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/about-multi-state-boxes.md ## Article Content: # Velo: About Multi-State Boxes Use multi-state boxes to display different content for different situations. For example, you can: - Show different site content if a shopping cart is empty or full. - Display an error state when something goes wrong. - Show a preloader while a page is loading. - Switch between "sale," "new," and "out of stock" badges for your products. - Show different site content depending on whether there's an upcoming event.
You can also use multi-state boxes to let site visitors easily navigate multiple displays. For example, visitors can: - Navigate what appear to be multiple tabs. - Switch between a brief description and a detailed block of text.
Multi-state boxes generally contain two or more states, each with different content. A specific state is displayed either because a particular condition exists (an empty shopping cart), or because a site visitor chose to navigate to that state (clicked a button to read expanded text).
**Tip:** Changing the box state might [impact your page layout](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/formatting-layout/how-page-layout-is-affected-when-elements-change-size.md).
## See also - Learn how to [set up your multi-state box](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md). - Learn how to [work with your multi-state box and code](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Setting Up Your Multi-State Box ## Article: Setting Up Your Multi-State Box ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md ## Article Content: # Velo: Setting Up Your Multi-State Box
Visit the Velo by Wix website to onboard and continue learning.
A [multi-state box](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/about-multi-state-boxes.md) contains multiple states with different content, and displays one state at a time. Add a multi-state box to your site and set up each state. Then [add code](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md) to define when each state will appear in your site. ### Add a Multi-State Box **To add a multi-state box to your page:** Wix Editor: 1. Make sure Velo Dev Mode is [enabled](https://dev.wix.com/docs/develop-websites/articles/getting-started/resources/about-velo-by-wix.md#to-enable-velo-on-your-site). 2. Click **Add** ![](https://d2eyqiy4n03ve6.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2015/08/05/f5f1bce8-77f0-405c-8520-f147e1035fb1.png) on the left side of the Editor. 3. Click **Interactive**. 4. Drag a multi-state box element onto your page. Wix Studio: 1. If necessary, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) and then **Start Coding**. 2. Click **Add** ![Add icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/setting-up-your-multi-state-box-md__tmp_git-push-event18c2b4b9f1e04cbd18daa0d3fff592ee_velo-articles_wix-editor-elements-with-velo_images_add-icon.png) and select **Layout Tools > Multi-state Boxes**. 3. Drag a multi-state box element onto your page. ### Navigate Your States To navigate between the different states of your multi-state box in the editor, click the element, click the dropdown next to the **Multi-State Box** label, and select a state. Wix Editor:
Wix Studio: ![multi-state box studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/setting-up-your-multi-state-box-md__tmp_git-push-event18c2b4b9f1e04cbd18daa0d3fff592ee_velo-articles_wix-editor-elements-with-velo_images_multi-state-box.png) Now you can add content to and customize each state in your multi-state box. ### Set Up Your Multi-State Box You can customize the design and setup of your multi-state box and each state in your box.  #### Set Up Your Box * **Manage States:** Add, duplicate, delete, and change the order of your states. You can also edit the IDs of your states. [Learn more](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md). * ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/07/07/f02a0a76-9718-4b2a-a874-e5fbd4a5a39d/e2e9e303-c924-4d20-b3e1-c140d32cb2b9.png) **Settings:** Add animations for transitioning between states. [Learn more](https://support.wix.com/en/article/multi-state-box-settings). * ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/07/07/a583318c-dc34-4eb7-bdfb-fea1d97151da/f48acc51-2fc9-4e7f-88c1-b134999a6755.png) **Design:** Customize the border and shadow of your multi-state box. [Learn more](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md). * ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/08/20/f3b92254-d4ae-42a5-a415-9484d2784aab/f2ce3281-1bc3-4d77-b184-e510816053ab.png) **Animation:** Add an animation to your multi-state box. [Learn more](https://support.wix.com/en/article/choose-animation).
**Tip** Duplicate states and then edit them to keep a consistent design throughout your multi-state box.
#### Set Up Your States [Navigate](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md) to the state you want to edit and then do any of the following: * **Add content:** Add whatever content you want to each state including text, images, buttons, and videos. * **Change Background:** Choose a color, image, or video for the background of the state, and then customize background settings. You can also apply a background to other states. [Learn more](https://support.wix.com/en/article/change-background-7730640). * **Resize:** Click the multi-state box and drag one of the resize handles on the edge of the box.  Note that the width of the multi-state box applies to all states, but you can adjust the height for each individual state. ### Multi-State Box and State IDs Each multi-state box and state has an ID instead of a name. The IDs of multi-state boxes and their states are significant because you use them in code.
**Important** Changing the ID of a multi-state box or state will break any existing code that uses the old ID. If you change an ID, make sure to update any related code.
* You can view and edit the ID of your multi-state box in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). * You can view and edit the IDs of your states in the [Manage States](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md) panel. You can also do this in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). #### To change a multi-state box ID in the Properties & Events panel: 1. Click your multi-state box. 2. In the Properties & Events panel, click the **ID** field and enter the new ID. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/07/09/1a499ed4-6bd8-4145-8820-9f669c37c4e6/2feb84ff-9dd4-48b2-b1a1-c41a031b546b.png) #### To change a state ID in the Properties & Events panel: 1. Double click your multi-state box. 2. [Navigate](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md) to the state you want to change. 3. In the Properties & Events panel, click the **State ID** field and enter the new ID. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2019/08/20/3be0812e-c645-44d0-a1e2-ee7e52e86307/d25eb90e-315c-4efc-9761-003a7f371783.png) #### To change a state ID in the Manage States panel: 1. Click your multi-state box. 2. Click **Manage States**. 3. In the Manage States panel, click the state box that you want, then click **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png) **> Edit ID**. 4. Enter the new ID. ![Manage States panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/setting-up-your-multi-state-box-md__tmp_git-push-event18c2b4b9f1e04cbd18daa0d3fff592ee_velo-articles_wix-editor-elements-with-velo_images_manage-states.png) > > Now that you've set up your multi-state box, get it up and running with code. Click [here](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md) to see a tutorial on working with multi-state boxes and Velo. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Multi-State Boxes and Code ## Article: Working with Multi-State Boxes and Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md ## Article Content: # Velo Tutorial: Working with Multi-State Boxes and Code A [multi-state box](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/about-multi-state-boxes.md) contains multiple states with different content, and displays one state at a time. Each state corresponds to a specific situation or status. You need to add code to your site to define when each state is displayed. This article demonstrates how to set up your multi-state box using code from [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md). ## Set Up Your Multi-State Box To set up your multi-state box, follow this general procedure: 1. [Add](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md) a multi-state box to your page. 2. [Set up](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/setting-up-your-multi-state-box.md) your states. 3. Add [code](https://www.wix.com/velo/reference/$w.MultiStateBox.html) to define when each state will be displayed: 1. Define a condition (for example, a site visitor clicked a button, a product is out of stock). 2. Select your multi-state box using its ID. 3. Apply the [changeState()](https://www.wix.com/velo/reference/$w.MultiStateBox.html#changeState) function with the state you want to move to.   ## Example 1: Site Visitors Change the State This example shows how to set up your multi-state box so that site visitors can switch between states by clicking a button and then focus on the button in the currently displayed state to enhance keyboard and screen reader functionality for visitors with disabilities. We added a multi-state box to our page called `myStateBox`, with two states called `state1` and `state2`. Then we added a button to each state for switching between the states. >**Note** > You can view and change your multi-state box and state IDs in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md). ### State 1 Wix Editor: ![state1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7752a1ac284983f8cbfa1db804b7b048.png) Wix Studio: ![state2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3178f431f403ecad057a52a7c1dc6c73.png) * `myStateBox`: ID of my multi-state box. * `state1`: ID of the current state, State 1. * `button1`: ID of button to click to move to State 2. ### State 2 Wix Editor: ![state1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/63b0b1f87233e5336d19c8545da8563f.png) Wix Studio: ![state2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0f9601371fc7d40caea115f580d0b852.png) * `myStateBox`: ID of my multi-state box. * `state2`: ID of the current state, State 2. * `button2`: ID of button to click to move to State 1. ### Code We added the following code to our [page tab](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md): ```javascript $w.onReady(function () { $w("#button1").onClick(() => { $w("#myStateBox").changeState("state2"); $w("#button2").focus() } ); $w("#button2").onClick(() => { $w("#myStateBox").changeState("state1"); $w("#button1").focus() } ); }); ``` ### Understanding the Code - **Line 2-5**: Add an onClick event handler to `button1` in State 1. - **Line 3**: Run the [`changeState()`](https://www.wix.com/velo/reference/$w.MultiStateBox.html#changeState) function on the `myStateBox` multi-state box, moving it from State 1 to State 2. - **Line 4**: Run the [`focus()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/focus.md) function on the `button2` element displayed in State 2. - **Line 6-9**: Add an onClick event handler to `button2` in State 2. - **Line 7**: Run the [`changeState()`](https://www.wix.com/velo/reference/$w.MultiStateBox.html#changeState) function on the `myStateBox` multi-state box, moving it from State 2 to State 1. - **Line 8**: Run the [`focus()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/focus.md) function on the `button1` element displayed in State 1. ### Adapt this Scenario You can adapt this scenario to enable site visitors to: * Switch between a brief description and a detailed block of text. * Fill out a custom multi-step form. * Navigate what appear to be multiple tabs. ## Example 2: State per Status >**Note:** >The images in this example show the multi-state box in the Wix Editor. The multi-state box in Wix Studio functions similarly to Wix Editor, with the only difference being its appearance, as demonstrated in the example above. This example shows how to set up your multi-state box with code so that a different state is displayed depending on a particular status. The example uses Wix Store products, but you can adapt the example for other scenarios. We added a multi-state box to our Wix Stores [product page](https://support.wix.com/en/article/customizing-your-wix-stores-product-page) that displays a different badge depending on the product's status. We added 3 states to our multi-state box: * Out of Stock * On Sale * Featured ### State 1: Out of Stock
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/10/25/7bc07b83-f67f-4522-801e-66477cd5c59e/8ad62ecf-4117-43c2-8d9a-17df21e31e69.png)
* `badgeStatebox`: ID of my multi-state box. * `outOfStock`: ID of the current state. For products that are out of stock. ### State 2: On Sale
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/10/25/84b78ce0-f0cb-4bca-80fa-a1e6dac279e1/068ac0e1-66e9-4d3f-8e30-e9849b50c7eb.png)
* `badgeStatebox`: ID of my multi-state box. * `onSale`: ID of the current state. For products that are on sale. ### State 3: Featured
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/10/25/3c614f0c-dfa5-4393-83ee-1a8b1ef39eaa/54b1d32f-b100-46b1-b0c9-1d549fa17df8.png)
* `badgeStatebox`: ID of my multi-state box. * `featured`: ID of the current state. For products that are not on sale or out of stock. ### Code Then we added the following code to the [page tab](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) of our [product page](https://www.wix.com/velo/reference/$w.ProductPage.html): ```javascript $w.onReady( function () { wixLocationFrontend.onChange(() => { $w('#myProductPage').getProduct() .then( (product) => { if (!product.inStock) { $w('#badgeStatebox').changeState("outOfStock"); } else if (product.price !== product.discountedPrice) { $w('#badgeStatebox').changeState("onSale"); } else { $w('#badgeStatebox').changeState("featured"); } } ) .catch( (error) => { console.log(error); } ); } ); } ); ``` ### Understanding the Code **Line 2**: Use the [`wixLocationFrontend.onChange()`](https://www.wix.com/velo/reference/wix-location.html#onChange) function to check when a site visitor navigates to a new product on the product page. When the current product changes, do the following: **Line 3**: Get all the information about the current product using the [`getProduct()`](https://www.wix.com/velo/reference/$w.ProductPage.html#getProduct) function. **Lines 5-6**: If the current product is not in stock, change the multi-state box's state to `outOfStock` to display the Out of Stock badge. **Line 8-9**: If the current product's discounted price is different than the regular price, this indicates that the product is currently on sale. If the product is on sale, change the multi-state box's state to `onSale` to display the On Sale badge. **Lines 11-12**: If the product is neither out of stock nor on sale, change the multi-state box's state to `featured` to display the Featured badge. **Lines 15-16**: If an error occurs while getting the product information, log the error to the console.
**Tip** If you want some products not to display a badge at all, you could [hide()](https://www.wix.com/velo/reference/$w.MultiStateBox.html#hide) the multi-state box when particular conditions are met.
### Adapt this Scenario You can adapt this scenario to: * Show different site content if a shopping cart is empty or full. * Display an error state when something goes wrong. * Show different site content depending on whether there's an upcoming event. * Display a preloader while the page is loading. ## Learn More * [$w.MultiStateBox API](https://www.wix.com/velo/reference/$w.MultiStateBox.html) * [$w.State API](https://www.wix.com/velo/reference/$w.State.html) * [About Multi-State Boxes](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/about-multi-state-boxes.md) * [Use a multi-state box to expand text with a Read More link](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/multi-state-boxes/working-with-multi-state-boxes-and-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with the HTML iframe Element ## Article: Working with the HTML iframe Element ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md ## Article Content: # About HTML iFrame Elements HTML iframe elements allow you to embed raw HTML or another website within your page. In general, you don't have access to the HTML and full code of your site's pages. This can be limiting when you want to add 3rd-party code to your site. Using an HTML element you can sometimes overcome these limitations by embedding raw HTML to your page. ## Supported IDEs You can embed HTML iframe elements in the [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). You can view and access any HTML iframe elements created in the editor when using other IDEs, such as the Local IDE. The IDEs synchronize with the editor to ensure consistency. You can also add more detailed code using: - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli/about-git-integration-wix-cli.md) (Wix Studio and Wix Editor). ## How HTML iFrame Elements Work The HTML element creates a sandboxed environment (an iframe) that does not have direct access to the other elements on your page. This means that widgets or libraries that need access to the rest of your page will not work in an HTML element. You can, however, pass data between your page code and the code in an HTML element so you can use it in all sorts of situations to do things you can't normally do directly. You can see a working [example site](https://www.wix.com/velo-examples/hello-html-comp) of an HTML element that passes data to and from the page. Open the [Hello HTML Component template](https://manage.wix.com/edit-template/from?originTemplateId=d39789d7-13ee-4208-8467-8a939cd8ed8c&templateId=cfd24f07-e15f-4091-b110-2b8b889fa1e3&autoDevMode=true) in the editor to work with site's code. To learn more about using HTML elements, see [Embedding a Site or a Widget](https://support.wix.com/en/article/wix-editor-embedding-a-site-or-a-widget). ## What You Can Embed You can embed the following elements using an HTML element: - [Sites](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-site-in-an-html-i-frame-element.md): Display an external site page within an element on your page. - [Code](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-code-widget-in-an-html-i-frame-element.md): Embed your own HTML code, or functionality from an external provider, for example a badge, a newsletter signup form, or a YouTube video. For an example of how to create a widget within an HTML iframe component, see [Creating a Rich Text Editor Using the HTML Component](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/creating-a-rich-text-editor-using-the-html-component.md). ## Using a JavaScript Library There are many JavaScript libraries that you can use in an HTML element. The library you add will only be able to interact with the elements inside your HTML element. To interact with the library from your page code or to interact with your page elements from the library, you need to use the messaging model described in [Messaging Between a Site Page and an HTML Element](https://dev.wix.com/docs/velo/velo-only-apis/$w/html-component/messaging-between-a-site-page-and-an-html-element.md). You can see a working [example site](https://www.wix.com/code-examples/v2-embed-chart) of an HTML element that uses the [Chart.js](http://www.chartjs.org/) library to create a chart. Open the [Custom Chart template](https://manage.wix.com/edit-template/from?originTemplateId=775c65bf-60ee-49aa-97dd-a171e369e3b1&templateId=b4662007-17e0-4fca-a4e6-2b774b16871a&autoDevMode=true) in the editor to work with site's code. ## See Also - [Embedding a Site in an HTML iFrame Element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-site-in-an-html-i-frame-element.md) - [Embedding a Code Widget in an HTML iFrame Element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-code-widget-in-an-html-i-frame-element.md) - [Tutorial: Creating a Rich Text Editor Using the HTML Component](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/creating-a-rich-text-editor-using-the-html-component.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Embedding a Site in an HTML iFrame Element ## Article: Embedding a Site in an HTML iFrame Element ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-site-in-an-html-i-frame-element.md ## Article Content: # Embedding a Site in an HTML iFrame Element You can embed an entire website within an [HTML iFrame element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md). Typically, when you want to embed another site in your site, you add an HTML element and use its settings to add the address of the website you want to embed. For example, to embed a [Wikipedia](https://www.wikipedia.org/) page in your site, add an HTML element to your page and set its **Website Address** to be the URL of the page you want to embed.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a83464186a3bd2fb7bcb92d0c292afdc)
**Warning:** The site you embed using the instructions above must have a URL that begins with HTTPS, not HTTP. If you try to embed a site with an HTTP URL, it may work in preview mode, but it will not function on your published site.
To embed a site with a URL that begins with the HTTP protocol, embed the site using the HTTPS protocol: 1. From the **Embed* code* section of the **Add** menu, select **Embed Site**. The element is added to your page. 1. In the HTML element's settings panel, select **Website Address**. 1. Enter the URL of the website you want to embed (with an "s" added to the "http"). ## See Also - [About HTML iFrame Elements](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md) - [Embedding a Code Widget in an HTML iFrame Element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-code-widget-in-an-html-i-frame-element.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Embedding a Code Widget in an HTML iFrame Element ## Article: Embedding a Code Widget in an HTML iFrame Element ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-code-widget-in-an-html-i-frame-element.md ## Article Content: # Embedding a Code Widget in an HTML iFrame Element You can embed code within an [HTML iframe element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md) to address a wide range of scenarios. If you want to embed a widget from a third party in your site, you'll need to add some code provided by the widget's creator.  Note that widgets that need to collect information about the Wix elements on your page will not work when their code is pasted into an HTML element. For example, [Google's Website Translator](https://translate.google.com/manager/website/) is a widget that translates the contents of your page into another language. Since it needs to know about your page elements to translate them, you can't add it to your site using an HTML element and have it translate the rest of your page. You can, however, add it to an HTML element and have it translate the contents of the element itself. Widgets that do not need to know any information about your site will usually work when their code is pasted into an HTML element. For example, the [weatherwidget.io](https://weatherwidget.io/) Current Weather Widget is a widget that displays the current weather. Since it doesn't need to know anything about your page elements, you can add it to your site using an HTML element. To add a code widget to your page: 1. From the **Embed code** section of the **Add** menu, select an **Embed code** element (Wix Studio) / **Embed HTML** element (Wix Editor). The element is added to your page. 1. Go to the relevant widget creator's website and collect a code snippet. 1. In the HTML element's settings panel, select **Code** and paste the widget code. ## See Also - [About HTML iFrame Elements](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element.md) - [Embedding a Site in an HTML iFrame Element](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/embedding-a-site-in-an-html-i-frame-element.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with the Progress Bar Element ## Article: Working with the Progress Bar Element ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/progress-bar/working-with-the-progress-bar-element.md ## Article Content: # Velo: Working with the Progress Bar Element
Visit the Velo by Wix website to onboard and continue learning.
A [progress bar](https://support.wix.com/en/article/cms-adding-and-setting-up-a-progress-bar) allows you to visually display the status of an ongoing process. Some common uses for progress bars are to display: * How much of a process a user has completed. * How many stages of a purchase process have been completed. * How many tasks are marked as done on a to-do list. * Percentage completed of a user profile.  * How much progress has been made towards a goal. * The amount of money raised towards a fundraising goal. * The number of people who signed a petition. Progress is measured as a number between 0 and the target value that you set. The current progress status is referred to as the value of the progress bar. The area indicating the progress that has already been made is colored in the foreground color and the remaining area until the target value is colored in the background color. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/07/16/e054245d-5ce0-4654-8118-0bc66e8eaff3/b3bcaa83-ad1b-47d1-b46b-a23d5d0e5ac6.png)
### API The progress bar's API allows you to get or set the value and target value. You can also set the styling of the background color, foreground color, border color, and border width. To learn more, see [ProgressBar](https://www.wix.com/velo/reference/$w.ProgressBar.html) in the API Reference. ```javascript // Set the target value as 50 $w('#myProgressBar').targetValue = 50; // Set the progress as 20 $w('#myProgressBar').value = 20; ``` ### Indicating Progress To indicate progress in a progress bar, you change the progress bar's value in code. Sometimes you simply change the value to a new value. In other cases, you need to first calculate the new value before setting it.  #### Example: Profile Completion Let's say you're using a progress bar to indicate how much of a profile a user has completed. After every step the user completes, you can change the progress bar's value to one more than the current value.  #### Example: Fundraising Goal Let's say you're using a progress bar to show how much money has been raised. Typically, you need to calculate that value by [running an aggregation](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/aggregations/working-with-aggregations-in-the-data-api.md) on a database collection where you store individual contributions as items. Then, use the result of the aggregation to set the value in the progress bar.  You can do this on-the-fly when displaying the progress bar using only code. Or you can perform the aggregation each time a contribution has been made and store that value in a collection. Then, you can [connect](https://support.wix.com/en/article/connecting-page-elements-to-a-dataset) the progress bar to that stored value using a dataset. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Forms ## Article: Working with Forms ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/forms/working-with-forms.md ## Article Content: # Velo: Working with Forms You can work with Forms using Velo APIs in 2 different ways:  * Use the Wix Forms app and its APIs. * Create your own custom form with input elements and submit buttons. Then use Velo to save the data to a collection. Let's present each approach so you can decide which to use. ## Wix Forms APIs Wix Forms is an app solution that provides you with a built-in suite of form functionalities, such as an out-of-the-box submission button, database collection creation, and auto-generated emails after submission. First use the [Wix Forms app](https://support.wix.com/en/article/about-wix-forms) to set up your forms quickly and efficiently, taking advantage of Wix Forms features. You even get a starter [collection](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) for your form that you can use to store the form data. Then use the [WixForms Velo APIs](https://www.wix.com/velo/reference/$w/form) to customize the visitor experience even more. For example, you can display a personalized thank you message when a visitor submits the form.
Check out the benefits of the Wix Forms app! * Choose from a variety of designs for your form from a set of templates. * You can design and customize the layout of the form in the Editor.  * Each form design comes with its own collection for storing form data. You don't have to create your own from scratch. * Each field on your form is automatically connected to the collection. You don't have to connect fields one by one. * The Wix Forms app sets up email notifications so you don't have to. The information is saved for your contacts, and goes through the Inbox (Wix CRM). * The Wix Forms app makes it easy to set up payment forms, multi-step forms, and subscription forms.
> > Without a Premium Plan, the Wix Forms app has limited features and capabilities. Learn more about [upgrading your Wix Forms app with a Premium Plan](https://support.wix.com/en/article/wix-forms-upgrading-wix-forms-with-a-premium-plan). ### Wix Forms starter collections When [you add the Wix Forms app](https://support.wix.com/en/article/adding-and-setting-up-a-form-on-your-site) to your site, you pick the type of form you want. Depending on the form you pick, you get a collection specifically built for that form. In addition to the form appearing in the Form Submissions tab in the Dashboard's Customer Management area, you can also see the collection in the Database section of the Code sidebar. You can use the collection "as is" or you can change the collection to meet your needs. For example, when we add a Donations form to our site, we get a starter collection called Donors with basic fields. Here we customized the collection by adding a Signature field. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2020/07/09/959cf02a-5169-499b-b603-ca81c15f0963/bddc7123-0703-4035-91ae-997a14bb504c.png)
The fields on the Donations form are connected to the fields in the Donors collection. This means that each time a visitor submits a form, the form field data is stored in the corresponding field in the collection.  > > The opposite is not true. Adding a field to a starter collection does not add a corresponding field to the form. ### The WixForms API 1. [Add and set up a Wix Form](https://support.wix.com/en/article/adding-and-setting-up-a-form-on-your-site) on your site in the Wix Editor. 2. Enable [Velo Dev Mode](https://dev.wix.com/docs/develop-websites/articles/get-started/quick-start.md). You will see a WixForms **wixForms1** element and its corresponding form element **form1** on your page. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2020/07/08/75ab0794-4a76-4011-b701-b3f9ac1c1974/b4845dda-020d-4a5b-81b7-89c6d3a553fe.png) The Form element is a container for the input elements and buttons in the Wix Form. If you hide or collapse either the WixForms or Form element, the other element is also hidden/collapsed. Note that we apply the WixForms functions and events to the WixForms element, not the Form element. 3. **Client-side event handlers:** Write the code for the client-side event handlers [onWixFormSubmitted( )](https://dev.wix.com/docs/velo/velo-only-apis/$w/wix-forms/on-wix-form-submitted.md) and [onWixFormSubmittedError( )](https://dev.wix.com/docs/velo/velo-only-apis/$w/wix-forms/on-wix-form-submitted-error.md) to handle what happens when a visitor submits the form. onWixFormSubmitted() provides information, such as field names and field values, that is available only on the client side. You can code operations to run on the client side, such as displaying a message after form submission. This event fires when the server indicates that the submission was received, even if the server is still asynchronously processing. Here is sample code for displaying a personalized message on the page thanking the visitor for a donation. ```javascript $w("#wixForms1").onWixFormSubmitted( {fields} => { let firstName = fields[0].fieldValue; let lastName = fields[1].fieldValue; let donation = fields[2].fieldValue; let email = fields[3].fieldValue; $w('#text1').text = `Thank you, ${firstName} ${lastName}, for your generous donation of ${donation}.`; }); /* fields array of objects: * [ * { * "id":"inputFirstName", * "fieldValue":"Maria", * "fieldName":"Enter first name" * } * { * "id":"inputLastName", * "fieldValue":"Santora", * "fieldName":"Enter last name" * }, * { * "id":"inputDonation", * "fieldValue":"1000", * "fieldName":"Enter donation amount" * }, * { * "id":"inputEmail", * "fieldValue":"ms@theCompany", "fieldName":"Enter email" * } * ] */ ``` 1. **Backend Event Handler:** Write the code for the backend [onFormSubmit()](https://www.wix.com/velo/reference/wix-crm-backend/events/onformsubmit) event handler function to perform operations on the server side when the visitor submits a form. \`onFormSubmit()\` provides additional information that the page does not have, such as the form's submission time and the contact ID. Keep in mind that the functions on the server run asynchronously and do not hold up onWixFormSubmitted( ) operations. ## Custom forms To provide your site visitors with a fully customized form experience that you totally control, you can create custom forms manually. Add user input elements and buttons to the page, and code their usage to act like a form.  Using Velo APIs to create your own custom forms may take a bit more time and coding than using the Wix Forms app, but customizing your own forms can offer some advantages, such as enabling additional field types and using advanced functionality with code, without needing a Premium Plan. ### Use APIs for custom forms 1. Use standard $w input elements and buttons to create your form. The user input elements on your form can use functionality provided by [WixForms](https://dev.wix.com/docs/velo/velo-only-apis/$w/wix-forms-v2/introduction.md).  2. Write code for the event handlers to customize your visitors' experience.  Here are some examples and tutorial articles to help you get started.  **Tutorial Articles** * [Sending an Email on Form Submission](https://support.wix.com/en/article/corvid-tutorial-sending-an-email-on-form-submission) * [Creating a Custom Registration Form with Code](https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/forms/working-with-forms.md) **Example sites and templates** - Multistage Forms ([example site](https://www.wix.com/code-examples/v2-multistage_form) | [template](https://manage.wix.com/edit-template/from?originTemplateId=472a1901-f9e2-4e1d-851f-eefb9f41b66b&templateId=0dc1daf7-3df9-4215-92cf-af382e8bb4c0&autoDevMode=true)) - Custom Validations ([example site](https://www.wix.com/code-examples/show-v2-custom-valid) | [template](https://manage.wix.com/edit-template/from?originTemplateId=d50002c3-0e20-4f3f-ba56-92a4b12a4a1a&templateId=b5a305df-bb8b-46bc-adb0-c1aab287d99c&autoDevMode=true)) - Collapsing Forms ([example site](https://www.wix.com/code-examples/show-v2-collapsing-f) | [template](https://manage.wix.com/edit-template/from?originTemplateId=3d745ed0-79ee-41ec-ab2e-9a36e072ec91&templateId=6ed673e9-315d-44b0-8dd0-1e877ea3aec7&autoDevMode=true)) - Using reCAPTCHA with Forms ([example site](https://www.wix.com/velo-examples/captchaexamp-vw) | [template](https://manage.wix.com/edit-template/from?originTemplateId=3f901ca5-31f5-40d0-a455-5735d0b6ade4&templateId=29d3fa87-469e-4464-a060-a88a3bd51577&autoDevMode=true)) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Wix Data Management Solutions ## Article: About Wix Data Management Solutions ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/about-wix-data-management-solutions.md ## Article Content: # About Wix Data Management Solutions Wix offers a variety of data management solutions to accommodate common user needs. This includes support for internal databases hosted directly on Wix as well as for external databases hosted outside the Wix ecosystem. This article explains the internal database capabilities Wix provides, and the available options for working with external databases. ## Supported IDEs You can work with Wix's data management solutions in the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## The Wix Content Management System Wix's built-in data management solution is the [Wix Content Management System (CMS)](https://support.wix.com/en/article/cms-content-management-system-an-overview). CMS data collections are document-oriented databases, optimized to store and retrieve websites’ content. They are globally replicated and have native support for PII encryption and GDPR. They run on secure, shared infrastructure, and are fully managed by Wix. CMS data collections can be managed via the [Wix Data APIs](https://dev.wix.com/docs/sdk/backend-modules/data/introduction.md), and used (with or without [datasets](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md)) to populate Wix UI elements like [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-a-table). For more information about using Wix Data to work with the CMS on a Wix site, see [Working with Wix Data](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-wix-data.md). ## Support for external databases While CMS collections cover a wide range of use cases for content-driven websites and applications, some projects may have specific requirements that can't be addressed by the integrated database solution. To address such cases, Wix allows users to connect an external database to Wix sites using an external database adaptor. Once the connection is set up, users can interact with these databases and use them to populate site elements as though they were Wix CMS collections. For more information about connecting to external databases, see [Integrating External Databases with Your Wix Site](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md). ## Choose the right database engine for your site workload If you haven't already chosen a database engine, there's a lot of material on the internet about database comparisons and benchmarks that can help you decide. We suggest taking the following into account: - Use the Wix-integrated [CMS](https://support.wix.com/en/article/cms-content-management-system-an-overview) collections for: - CRUD operations for 1 to 10k items or up to 100k items, depending on the size of the data - Query operations of 1 to 10k items while using indexes. - Use relational databases for: - User facing operational workloads, including aggregates and data consistency validations. They scale according to the actual data and capacity planning for specific workloads - Use analytical databases for: - Reporting or analytical workloads when the dataset is mainly used for reporting or aggregates and, the dataset size exceeds the capacity of the relational database instance - Dedicated analytical databases include Google BigQuery or Snowflake. > **Note:** Wix created out-of-the-box container image adaptors for a number of databases that can run on AWS, Azure, and Google Cloud Platform. Using these out of the box adaptors can make integration with the supported databases simpler. See which databases are [supported with an out-of-the-box adaptor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md#deploy-an-out-of-the-box-external-database-adaptor). ## See also - [CMS (Content Management System): An Overview](https://support.wix.com/en/article/cms-content-management-system-an-overview) - [Working with Wix Data](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-wix-data.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Wix Data ## Article: Working with Wix Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-wix-data.md ## Article Content: # Velo: Working with Wix Data [Wix Data](https://support.wix.com/en/article/welcome-to-wix-data) lets you work with database collections, user input elements, and dynamic pages on your site. This article explains how you can enhance Wix Data functionality using [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md) or with [Wix Studio](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). ## Enable Wix Data To enable Wix Data: **Wix Editor**: - Make sure Velo Dev Mode is [enabled](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md). - Click the **Add** icon ![Add](https://d2eyqiy4n03ve6.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2015/08/03/e486e9b2-0bf2-4a17-8fd7-4445d938d6c3.png) on the left. Click **CMS**, then click **Start Now**. Once enabled, your database collections appear in the **Databases** panel of the Velo sidebar, and your dynamic pages appear in the **Page Code** panel. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b26d22c561e999d1737ee6c1fb282c89.png)
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5e6485644fcbe9d657adbbb293433a7f.png) > **Note** > Dynamic pages are grouped in the Velo sidebar according to dynamic page URL prefixes. [Learn more](https://support.wix.com/en/article/velo-making-dynamic-page-urls-meaningful-with-prefixes). **Wix Studio**: - If you haven't done so already, click ![Code icon](https://github.com/wix-incubator/wix-code-docs/assets/50321691/2c41d3df-930f-4e0f-966f-038742adceed) on the left, then click **Start Coding**. - Click **Add** ![Add](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0edf787becd53682d716f1c9c6a56fc1.png) on the left. Click **CMS**, then click **Start Now**. Once enabled, your database collections appear under **Your Collections** in the **CMS** panel, and your dynamic pages appear in the **Pages** panel. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/087441697cb69d3327e710b26504cea7.png)
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/275d8e90c193e195b92079ae0bc54ea3.png) > **Note:** > Enabling Velo Dev Mode (Wix Editor) or code (Wix Studio) automatically enables Wix Data.  > However, enabling Wix Data does not enable Velo Dev Mode or code. ### Managing Collections and Dynamic Pages From the sidebar, you can perform the following Wix Data actions:
Manage Collections - **Create a collection:** - Wix Editor: Click **Databases** and hover over Your Collections, click the plus![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2018/12/09/7908fa24-67cc-4489-969b-210529b83321.png) icon, and click **New Collection**. - Wix Studio: Click **CMS**, then click **Create Collection**. - **Edit a collection:** - Wix Editor: Click **Databases**, and click the collection. - Wix Studio: Click **CMS**, navigate to the **Your Collections** tab and click the collection. - **Edit collection permissions:** Hover over a collection and click the **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/9e7506ba-4048-4864-b26e-afcb489ae6d5/1665a276-48df-4061-92aa-c17d5df6cdb2.png) icon. Click **Edit Settings**, then click the **Permissions & privacy** tab. Select a permission preset or **Custom** from the dropdown menu and then set the **Custom Content Permissions**. - **Delete a collection:** Hover over a collection, click the **Show More** icon, and click **Delete Collection**.
Manage Dynamic Pages - **Add a dynamic page:** In **Databases** (Wix Editor) or **CMS** (Wix Studio), hover over a collection, click the **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/051f9df6-b1cb-4e3d-983d-0b674e3f9dba/f22e2f9f-f84b-4445-ab63-fe0400797e98.png) icon, and click **Add dynamic page**. - **Rename a dynamic page:** In **Page Code** (Wix Editor) or **Site Pages** (Wix Studio), hover over the dynamic page, click the **Show More** icon, and click **Rename**. - **Convert a dynamic page to a regular page:** In **Page Code** (Wix Editor) or **Site Pages** (Wix Studio), hover over the dynamic page, click the **Show More** icon, and click **Remove Connections**. - **Delete a dynamic page:** In **Page Code** (Wix Editor) or **Site Pages** (Wix Studio), hover over the dynamic page, click the **Show More** icon, and click **Delete**.
View Page Settings - **View page settings:** In **Page Code** (Wix Editor) or **Site Pages** (Wix Studio), hover over a regular or dynamic page, click the **Show More** icon, and click **Settings**.
### Collections With Wix Data you can create and manage database collections on your site. Velo lets you expand your capabilities when working with collections. Keep the following information in mind when working with collections and code: #### Field IDs When you add a field to your collection, a Field ID is automatically created based on the Field Name. Field IDs are only visible if Velo Dev Mode (Wix Editor) or code (Wix Studio) is enabled. You can specify your own Field ID, if you wish.
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bf3648e2dc43489e8d86463b249fdbf3.png)
> **Important** > > - You cannot change the Field ID once the field has been created. > - Field IDs cannot begin with $ or contain a period '.' Field IDs are used when working with fields in code using the [Data API](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md) or [Dataset API](http://wix.to/94BuAAs/wix-dataset.html). For example, if you want to insert an item into a collection using the Data API, you can use the following code: ```javascript wixData.insert("MyCollectionName", { someFieldKey: "someValue" }); ``` #### Hooks The [Data API](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md) contains hooks that allow you to run code before or after you interact with a collection. To add a hook to a collection, hover over the collection name in the **Databases** panel (Wix Editor) or the **CMS** panel (Wix Studio) on the left, click the **Show More** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/37ff9473-4cf0-4d6b-845e-d93462da44eb/2c4c1bdd-afc8-407e-a434-7e47492e2ba6.png) icon and select **Add Data Hooks**. Learn more about [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md). #### Non-Schema Fields When you add fields to your collection using code, the fields are not automatically added to the collection structure and must be added manually. In the CMS, these fields appear with a warning icon, indicating that the field isn't defined yet. Learn more about the [structure of your database collection](https://support.wix.com/en/article/about-the-structure-of-your-database-collection). You can click on the field name to define the field type and add it to the structure.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2020/04/16/c086062c-507c-48e2-a000-d9f0598455c5/a2606c49-b54e-4592-99a8-e80a6ce37da7.png)
#### Removing and Restoring Collections If you remove a collection that is referenced in code, the code will no longer work and will generate an error in the Editor and in the published site. You can replace the collection name with a different one if relevant. If you restore a collection you removed, the code will work again. #### Data APIs The Velo [Data API](https://www.wix.com/velo/reference/wix-data.html) and [Dataset API](https://www.wix.com/velo/reference/wix-dataset.html) provide additional functionality for working with data collections in your site. You can create customized searches and filters, manage and manipulate your data, and work with pagination, permissions, and hooks. Learn more about [working with the Data API](https://support.wix.com/en/article/velo-working-with-the-data-api). #### Data Quotas Wix places quotas on the amount of data resources your site can use. These quotas affect things like the number of collections your site can have and the number of data requests your site can make per minute. If your site requires additional resources, Wix offers a variety of [premium plans](https://www.wix.com/upgrade/website) to suit your needs. To find out more about the [data resources](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) available for each package. Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). **Note:** If you have Blocks apps installed on your site, how they affect your quotas depends on whether they are private or public apps. Learn more about [Blocks apps and CMS quotas](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md#blocks-apps-and-collection-quotas). #### Request timeouts When your site makes a data request, it may take some time to receive a response. Wix Data places limits on how long a response can take before the request times out. If the response time exceeds this limit, Wix Data returns an error instead of the intended result. Request timeouts vary depending on your [Wix plan](https://www.wix.com/plans) and the type of collection you are attempting to access: | Collection Type | Wix plan | Timeout | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ---------- | | [CMS collections](https://support.wix.com/en/article/cms-formerly-content-manager-managing-your-collections) | Free sites, premium sites (excl. Elite and Business Elites) | 5 seconds | | [CMS collections](https://support.wix.com/en/article/cms-formerly-content-manager-managing-your-collections) | Business Elite & Elite Premium | 10 seconds | | [External Database Collections](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md) | All plans | 15 seconds | After the specified timeout, the request fails and the following error message is displayed in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/about-logs.md): `WDE0028: Operation time limit exceeded.` Learn more about [Wix Data error codes](https://dev.wix.com/docs/velo/apis/wix-data/error-codes.md). ### User Input With Wix Data you can create user input forms and store user data in your collections. Velo provides additional layers of functionality when working with user input elements: #### Input Validation When collecting user input data, you want to make sure that the data entered is valid. Some input validation is available via the user input element Settings panel, but with code you can provide additional validation measures. Learn more about [input validation](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/about-validating-user-input-with-code.md). #### Convert Regular Elements into Submit Buttons Without code, you can create a form using [user input elements](https://support.wix.com/en/article/about-user-input-elements) and a "submit" element that saves the data into your collection when clicked. Only selected elements, such as buttons and images, can act as submit elements. With code, you can add submit functionality to elements without a Submit option in the Connect panel. You can also add submit [success and error messages](https://support.wix.com/en/article/velo-adding-success-and-error-messages-for-elements-without-a-submit-option) when using those elements to submit. #### Create a Custom Signup Form With Velo you can create your own [custom signup form](https://support.wix.com/en/article/velo-tutorial-creating-a-custom-registration-form-with-code) to replace the standard one. Then [enable custom signup](https://support.wix.com/en/article/velo-enabling-custom-site-registration) so your visitors are directed to your custom form for site registration. ### Dynamic Pages With Wix Data you can create dynamic pages to display content from your database collection on multiple pages in your site. You can also use Velo to interact with dynamic pages: #### Hiding Dynamic Pages in Mobile Without code, you can hide dynamic pages on your site when displayed on different devices such as a mobile device, by hiding the elements that link to the pages. Any code on your site connected to a hidden element will generate an error. Therefore it's best to use the [`formFactor`](https://dev.wix.com/docs/velo/apis/wix-window-frontend/form-factor.md) property of the Window API to determine if the visitor is viewing the site from a different device, and use the elements' `hide()` or `collapse()` functions to visually remove the elements from the page. Learn more about [hiding elements on different devices](https://support.wix.com/en/article/hiding-an-element-in-the-mobile-editor).
**Important:** Hiding an element using the mobile Editor (Wix Editor) or [breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) (Wix Studio) removes that element from the page completely. This causes any code that relies on the hidden element's existence to break.
#### Dynamic Pages and Hooks You can add hooks to dynamic pages: 1. Click **Menus & Pages**  ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d226e37c6d3758dfd97efce30f8683fc.png) (Wix Editor) or click **Site Pages** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb0ea95cfbee93a0602286780732a828.png) (Wix Studio) on the left side. 1. Click the **Show More** icon  ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1617580fa1f44c7a618a2ffb45634efc.png)  next to the dynamic page. 1. Click **Settings** and select the **Page Info** tab. 1. Open the **Advanced Settings** tab, then click **Add hooks**. 1. Select the hooks you want to add, then click **Add & Edit Code**. When a request comes in for one of your dynamic pages, a router uses the URL of the request to decide which page to show and what data to bind to the page's dataset. You can add a [data binding router hook](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks-for-dynamic-pages.md) to intercept this process at certain points and insert additional logic. ### Working with Wix App Collections in Your Code You reference the collections that Wix Apps add to your site similar to how you reference collections you create yourself, with one difference. To reference a collection in your code that you created yourself, you just use the collection name in the code. For example, this code retrieves an item with an ID of 00001 from the collection named myCollection: ```javascript wixData.get("myCollection", "00001"); ``` To reference a collection that a Wix App added to your site you need to also include the App name in the path of the collection name. For example, let's say you also want to retrieve an item with an ID of 00001, but this time from the PrivateMembersData collection inside the Members database.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/fbf05d8d-0a27-42a9-ba52-99787277835b/9b82d183-f5db-462a-a763-4005a85ef6d0.png)
In this case, you need to include Members in the path for the collection name, like this: ```javascript wixData.get("Members/PrivateMembersData", "00001"); ``` You can also use Wix Data to add and update information in some Wix App collections. For example: ```javascript let collectionToInsert = { title: "New Collection", }; wixData.insert("Portfolio/Collections", collectionToInsert).then((item) => { console.log(item); }); ``` You can find many examples using the Wix Editor in the **Business Solutions** section of the [Coding Examples site](https://dev.wix.com/docs/coding-examples.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with the Data API ## Article: Working with the Data API ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md ## Article Content: # Velo: Working with the Data API ### Introduction The [Wix Data API](https://www.wix.com/velo/reference/wix-data/introduction) lets you work with your database collections using code and gives you more capabilities than what is available in the editor alone. To learn more about managing your data without using the Data API and how the database is structured, see [About Database Collections](https://support.wix.com/en/article/about-database-collections). Wix Data includes functions that enable you to manage the data in existing collections, build queries on collections, and register hooks on different operations. With the [Collections API](https://www.wix.com/velo/reference/wix-data-v2/collections/introduction) you can create new collections and manage their structure. **Important:** - You must first create a collection either in the [CMS](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) or by using the [Collections API](https://www.wix.com/velo/reference/wix-data-v2/collections/createdatacollection) before you can work with it using the Wix Data API. - To use the Wix Data API, you need to import the module as follows: ```javascript // Add the following to the top of your code to import wixData: import wixData from "wix-data"; ``` - Wix places quotas on requests made by your site using the Wix Data API. These quotas affect things like the number of requests your site can make per minute and the amount of time your requests can run for. Learn more about [data quotas](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/legacy-data-compute-and-developer-tools-limits.md#legacy-plan-limits) and how to work with them. ### Wix Data API The Wix Data API allows you to manipulate the data in your collections and retrieve it in meaningful and useful ways. It contains: - `wix-data`: Functions that let you query and manipulate the data in your collections. - **Hooks**: Code you can set to run before or after you interact with a collection. Hooks are added to a collection in the Velo sidebar (Wix Editor) or CMS panel (Wix Studio) when you hover over the collection name. The code for the hooks are written in the data.js file which resides in the backend. - `WixDataFilter`: Functions for filtering a `WixDataQuery`. - `WixDataQuery`: An object that contains a query definition and functions that enable you to refine a query. - `WixDataQueryResult`: An object that contains a query's results, other information about the query, and pagination methods. - `WixDataSort`: Functions for sorting a `WixDataQuery`. > **Note:** > Wix places [quotas](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/legacy-data-compute-and-developer-tools-limits.md#legacy-plan-limits) on data requests made by your site, as well as the amount of data your site can contain. These quotas affect the number of requests your site can make per minute and the amount of time your requests can run for. Let's say you are building a database of customers who have registered on your online jewelry store. Your registration form would collect basic personal information about each customer. It might also ask customers for other information, such as relatives they may shop for, so you could know when to send them notifications of special offers and sales. You may also want to know about their general interests so that you can provide better purchase suggestions. When a customer makes a purchase, you would save the details of their purchase as well. An item in your collection may look like this: ```javascript let newCustomer = { name: "Mary", age: 47, _id: "mary.jones@myemail.com", phone: "(345)123-4567", pic: "Mary.jpg", labels: ["female", "adult", "hiking"], relatives: ["husband", "daughter"], purchases: [ { item: "watch", material: "gold", price: "2000.00" }, { item: "ring", material: "platinum", price: "500.00" }, { item: "bracelet", material: "silver", price: "250.00" }, ], }; ``` You would then add this item to your collection with the `insert` method: ```javascript wixData.insert("Customer", newCustomer); ``` As your customer list grows and your site has more traffic, as well as repeat visits from customers, you would use [`save()`](http://wix.to/94BuAAs/wix-data.html#save), [`update()`](http://wix.to/94BuAAs/wix-data.html#update), [`remove()`](http://wix.to/94BuAAs/wix-data.html#remove), and [`get()`](http://wix.to/94BuAAs/wix-data.html#get) to modify the items in your collection. It is important to familiarize yourself with the Wix Data model and [`query()`](http://wix.to/94BuAAs/wix-data.WixDataQuery.html) methods so that you can use the most effective data modeling when you create your collections. ### Working with Permissions in Wix Data When you use Wix Data methods in your Public and Pages files you need to consider the permissions your site visitors will have. Some methods modify data in collections while other methods only read data. Your site visitor needs to have the corresponding permissions for these methods to work. If a method is called with insufficient permissions the method will fail. For more information, see [About Database Collection Permissions](https://support.wix.com/en/article/about-database-collection-permissions). By default, your site visitor's permissions apply to Wix Data methods you call from your backend code also. However, you can call Wix Data methods from backend code without checking permissions by passing the method the `WixDataOptions` object as the `options` parameter with the property:value pair `suppressAuth: true`. ### Working with Hooks in Wix Data The `wix-data` module contains hooks that allow you to run code before or after you interact with a collection. See [How to Use Data Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md) to learn more. When a method is called in your code, the system first checks that the method was called with the correct permissions. If you registered hooks to a method, they will run only if the method passes the permissions check. You can call Wix Data methods from the backend without their registered hooks by passing the `options` parameter for the method a `WixDataOptions` object with the property:value pair `suppressHooks: true`. ### Querying Your Collection A collection of data is only useful if you can retrieve the data in meaningful ways. To query a collection, use the [`query`](https://www.wix.com/code/reference/wix-data.WixDataQuery.html) method. By default, `query` returns the first 50 items in a collection in descending order of their `_createdDate` value. Items that are marked as [hidden](https://support.wix.com/en/article/cms-controlling-live-site-item-visibility-from-your-collection?tabs=Editor#changing-item-statuses-to-visible-or-hidden) are not returned. For example, the following query logs to the console the first 50 items in the Customer collection, sorted in descending order of their `_createdDate` values: ```javascript wixData .query("Customer") .find() .then((results) => { console.log(results.items); }); ``` The [`find()`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#find) function is chained to the query, and it runs the query. The `find()` function returns a [Promise](https://support.wix.com/en/article/working-with-javascript-in-developer-tools#es2015-feature-support) that resolves to a [`WixDataQueryResult`](https://www.wix.com/velo/reference/wix-data.WixDataQueryResult.html) object. Because `find()` returns a Promise, `then()` is also chained to the query to display the results. To display the items that the query returns, use the `items` property of the `WixDataQueryResult` object. #### Request Timeouts When your site makes a data request, it may take some time to receive a response. Wix Data places limits on how long a response can take before the request times out. If the response time exceeds this limit, Wix Data returns an error instead of the intended result. Learn more about [request timeouts in Wix Data](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-wix-data.md#request-timeouts). ### Refining Your Query To refine the results of a query, chain [`WixDataQuery`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html) methods to the query. For example, let's say you want to query the Customer collection for all customers over the age of 20. You would chain the [`gt()`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#gt) (greater than) function to the query, like this: ```javascript wixData .query("Customer") .gt("age", 20) .find() .then((results) => { console.log(results.items); }); ``` The `query()` function returns a `WixDataQuery` object, which contains the definition of the query. Each `WixDataQuery` method also returns a `WixDataQuery` object. Since they both return the same type of object, you can chain multiple `WixDataQuery` methods onto a call to `query()`, which further refines the query results. For example, let's say you want to take the results of the previous query, but return only the male customers. You would add the `hasSome` method to your query, with `labels` as the `paremeterName` and `Male` as the value: ```javascript wixData .query("Customer") .gt("age", 20) .hasSome("labels", "Male") .find() .then((results) => { console.log(results.items); }); ``` This adds the `gt()` and `hasSome()` conditions to the query definition that is stored in the `WixDataQuery` object. Now when you call `find()`, it uses these conditions to run the query. ### Query Results Query results are returned in a `WixDataQueryResult` object. This object has the following properties: - `currentPage`: The index (zero-based) of the current results page number. - `items`: An array that holds the results of the query. - `length`: The number of items in the current results page. - `pageSize`: The query page size, based on the defined limit. - `query`: The `WixDataQuery` object used in the query. - `totalCount`: The total number of items that match the query. - `totalPages`: The total number of pages the query produces, based on the defined `skip` and `limit` values. For example, the following would display the number of results for the query for male customers over the age of 20: ```javascript wixData .query("Customer") .gt("age", 20) .hasSome("labels", "Male") .find() .then((results) => { console.log(results.totalCount); }); ``` ### Pagination `WixDataQueryResult` has [`next()`](https://www.wix.com/velo/reference/wix-data.WixDataQueryResult.html#next) and [`prev()`](https://www.wix.com/velo/reference/wix-data.WixDataQueryResult.html#prev) functions that enable you to easily page forward and backward in your results. For example, let's say you want to filter the Customer collection for all female customers 20 years or older who have purchased a ring. You also want each page of results to display 15 items. You use the [`limit`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#limit) method to limit the number of results a query returns. ```javascript let femaleRingTwenty; wixData .query("Customer") .ge("age", 20) .hasSome("labels", "Female") .eq("purchases.item", "ring") .limit(15) .find() .then((results) => { console.log(results.items); femaleRingTwenty = results; }); ``` This displays the first 15 results from the query, starting at the first result and ending at the 15th. To display the next page of results, you would call `next` on the results: ```javascript femaleRingTwenty.next().then(function (results) { console.log(results.items); femaleRingTwenty = results; }); ``` The `next()` function also returns a Promise, so you chain `then()` to the call to `next` and log the resolution of the Promise to the console, which returns the 16th through 30th results. Also, assign `femaleRingTwenty` the results of `next()` so that you can call `prev()` or `next()` on `femaleRingTwenty` to get the previous or next page of results. ### Manual Pagination with Prev and Next The `skip()` function enables `next()` and `prev()` to provide a page of query results that skips the first number of results from the total results that match the query. For example, when you query the collection in the pagination example above, it returns results with a `limit()` of 15, but it also has a default `skip()` value of 0. This returns the query results starting at the 1st result and ending at the 15th. When you then call `next()` for the first time, it returns results with a `skip()` value of 15 and a `limit()` of 15. This tells Wix Data to return the query results starting at the 16th result and ending at the 30th result. If you call `femaleRingTwenty.next()` again, it returns results with a `skip()` value of 30 and a limit of 15, returning results starting at the 31st result and ending at the 45th. When you call `prev()` and `next()`, they handle changing the `skip()` value to match the requested range of results. You can also manually assign a value to `skip()`. This would be useful if you want to enable jumping to a specific page of results without having to use `next()` or `prev()` multiple times. For example, using the query in the previous example, if you want to get the 5th page of results with 12 results per page, you would query your collection with a skip of 48 (limit \* (5-1)) and a limit of 12: ```javascript let queryResults; wixData .query("Customer") .ge("age", 20) .hasSome("labels", "Female") .eq("purchases.item", "ring") .skip(48) .limit(12) .find() .then((results) => { console.log(results.items); queryResults = results; }); ``` This logs the 49th through 60th results. If you then call `next()` or `prev()` on `queryResults`, they return the next or previous 12 results. ### Date Fields Wix Data supports 2 kinds of date fields: - **Date and Time** - **Date** These kinds of fields have different data types and should be used differently in your code. #### Date **Date** fields are represented as strings in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date format (YYY-MM-DD). When you retrieve an item with this kind of field, or write to one, you should use this format. #### Date and Time **Date and Time** fields are represented as JavaScript [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) objects. When working with this type of field, you should use this format. If you assign a **Date and Time** field a value of an ISO date string, Wix Data treats it as it would any other string. For example, you can use a JavaScript Date object as the value for a Date and Time field when creating a new Wix Data item. In this example, `birthday` is assigned a date: ```javascript let newCustomer = { name: "Mary", age: 47, _id: "mary.jones@myemail.com", pic: "Mary.jpg", labels: ["Female", "Adult", "Hiking"], relatives: ["husband", "daughter"], birthday: new Date(1975, 10, 5), }; // Use wixData.insert to add a new item to the Customer collection let entry = wixData.insert("Customer", newCustomer); ``` Wix Data always saves Date objects and displays them in the console as a string with a UTC timezone. For example, if you display the results of the previous insert: ```javascript entry.then((results) => { console.log(results); }); ``` it logs: ```javascript { name: "Mary", age: 47, _id: "mary.jones@myemail.com", pic: "Mary.jpg", labels: ["Female", "Adult", "Hiking"], relatives: ["husband", "daughter"], birthday: "1975-10-05T14:24:20Z", _createdDate: "2004-04-01T01:04:45Z", _UpdatedDate: "2006-02-15T12:35:20Z", } ``` If you need to convert one type of date data to the other, you can do the following. To convert ISO date strings to Date objects: ```javascript const dateString = "1985-11-23"; const dateObject = new Date(dateString); ``` To convert a Date object into an ISO date string: ```javascript const dateString = dateObject.toISOString().substring(0, 10); ``` ### Media Fields Wix Data supports video, image, and other common media files. One of the collection fields must be the media file's unique Media Manager URL. This URL has a specific format that varies based on media type and file specifications. #### Working with uploaded media using Wix Media V2 Follow these steps to upload and insert media files into your data collection using `wix-media.v2` and Axios. 1. Import the relevant API modules and packages. > **Notes**: > > - Axios needs to be [installed](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/about-npm-packages.md) in your site. > - The `wix-media.v2` methods require elevated permissions. ```javascript import axios from "axios"; import { files } from "wix-media.v2"; import { elevate } from "wix-auth"; ``` 2. Call [`generateFileUploadUrl()`](https://www.wix.com/velo/reference/wix-media-v2/files/generatefileuploadurl) to create and return the upload URL. ```javascript export async function myGenerateFileUploadUrl { const elevatedGenerateFileUploadUrl = elevate(files.generateFileUploadUrl); const result = await elevatedGenerateFileUploadUrl(mimeType, options); const uploadUrl = result.uploadUrl; return uploadUrl; } const uploadUrl = myGenerateFileUploadUrl(); ``` 3. Use the [Upload API](https://www.wix.com/velo/reference/wix-media-v2/files/upload-api) to make an Axios HTTP POST request. The request uploads the media file using the upload URL that was generated in the previous step. Pass in the upload URL as one of the request parameters. A successful request results in the media file being uploaded to your [Media Manager](https://support.wix.com/en/article/wix-media-about-the-media-manager). ```javascript const params = { filename: "testImage.jpeg", }; const headers = { "Content-Type": "application/octet-stream", Accept: "application/json", }; const uploadResponse = await axios.put(uploadUrl, myImageFileData, { headers, params, validateStatus: () => true, }); ``` 4. To get the compatible Media Manager URL, call [`getFileDescriptor()`](https://www.wix.com/velo/reference/wix-media-v2/files/getfiledescriptor). Pass the `file.id` property from the Upload API's [response object](https://www.wix.com/velo/reference/wix-media-v2/files/upload-api#wix-media-v2_files_upload-api_response) as the function's only parameter. ```javascript const elevatedGetFileDescriptor = elevate(files.getFileDescriptor); const fileDescriptorResponse = await elevatedGetFileDescriptor( uploadResponse.data.file.id ); ``` 5. Locate the Media Manager URL in the file descriptor response object. The Media Manager URL is located in the response's `media` property, however, the exact location of the URL depends on the [type of media file](https://support.wix.com/en/article/wix-media-supported-media-file-types-and-file-sizes?tabs=Images) that was uploaded. The following table illustrates where the Media Manager URLs are located in the `getFileDescriptor()` response object: | Media type | Property name | | ------------------ | -------------------- | | Audio files | `media.audio._id` | | Video files | `media.video` | | Image files | `media.image.image` | | Document files | `media.document` | | Vector image files | `media.vector.image` | | Archive files | `media.archive.url` | 6. Insert the Media Manager URL into your data collection. ```javascript const imageUrl = fileDescriptorResponse.media.image.image; const newMediaFile = { title: "myImage", src: imageUrl, }; // Use wixData.insert to add a new item to the data collection let entry = wixData.insert("MyMedia", newMediaFile); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Overview of the Wix Data and Wix Dataset APIs ## Article: Overview of the Wix Data and Wix Dataset APIs ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/overview-of-the-wix-data-and-wix-dataset-apis.md ## Article Content: # Velo: Overview of the Wix Data and Wix Dataset APIs Data is an integral part of many websites. Data refers to information your site displays or collects from site visitors. For example, real estate sites, sites with a store, and sites with a form all contain data. Velo provides you with many ways to manage and manipulate your site data. This article provides an overview of the different data operations you can perform using Velo. >**Note** > Wix places [quotas](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/optimizing-your-code.md#data-requests) on data requests made by your site, as well as the amount of data your site can contain. These quotas affect the number of requests your site can make per minute and the amount of time your requests can run for. ## Data Building Blocks There are 3 components involved in managing your site's data: * [Database collections](https://support.wix.com/en/article/about-the-content-manager-7160473#collections): Where you store your site's data. * [Datasets](https://support.wix.com/en/article/about-datasets-6368396): An element hidden from site visitors that acts as a connector between your collection and page elements. * [Page elements:](https://support.wix.com/en/article/connecting-page-elements-to-a-dataset) The elements on your site that display or gather data. For example, a [repeater](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids) displays data, and [user inputs](https://support.wix.com/en/article/about-user-input-elements) collect data from site visitors. ## No Code Required Before jumping into code, it's helpful to know that there are many data operations you can perform without any code. By connecting the content stored in your database collection with your page elements via a dataset, you can do the following without using any code: * [Display content](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) from your collection in page elements on your site. * [Filter and sort](https://support.wix.com/en/article/cms-formerly-content-manager-hiding-sorting-and-filtering-fields-in-your-collection-view) the content that is displayed in your page elements. * Display content from multiple collections using [reference fields](https://support.wix.com/en/article/displaying-content-from-multiple-content-collections-using-reference-fields). * [Create a custom form](https://support.wix.com/en/article/creating-a-form-with-user-input-elements) using user input elements and store site visitors' answers in a collection. * [Create dynamic pages](https://support.wix.com/en/article/about-dynamic-pages-4488730), which are multiple pages with the same design and layout but different content. * [Create an index page](https://support.wix.com/en/article/cms-adding-an-index-for-your-collection) for your dynamic pages. ## Dataset plus Code If you are using a [dataset](https://support.wix.com/en/article/about-datasets-6368396) to connect your database collection content with your page elements, you can extend your dataset's built-in capabilities by adding code from the [wix-dataset APIs](https://www.wix.com/velo/reference/wix-dataset) to your site.  The wix-dataset APIs are divided into 2 sections:  * [Dataset](https://www.wix.com/velo/reference/wix-dataset/dataset): For regular datasets.  * [DynamicDataset](https://www.wix.com/velo/reference/wix-dataset/dynamicdataset): For [dynamic datasets](https://support.wix.com/en/article/changing-your-dynamic-page-dataset-settings), special datasets for [dynamic pages](https://support.wix.com/en/article/about-dynamic-pages-4488730) that control how the  elements on your dynamic pages interact with the data in your collections. This section includes all the functions in the Dataset section, plus a couple additional functions specific to dynamic datasets. ### Dataset Mode The [mode](https://support.wix.com/en/article/working-with-dataset-connector-modes-and-collection-permissions#dataset-modes) of your dataset affects which dataset functions you can call. For example, if you set your dataset mode to **Read-only**, you won't be able to call the [save()](https://www.wix.com/velo/reference/wix-dataset/dataset/save) function on your dataset, since it writes from your dataset to your collection. ### Dataset onReady() Event Handler Most wix-dataset functions run properly only if they're called after the dataset has loaded its data. * If you are calling a dataset function in the page's [onReady()](https://www.wix.com/velo/reference/$w/onready) function, you should make sure that the dataset is ready by calling the function you'd like to use from inside the **dataset's** [onReady()](https://www.wix.com/velo/reference/wix-dataset/dataset/onready) function (not to be confused with the **page's** [onReady()](https://www.wix.com/velo/reference/$w/onready) function mentioned above). To check whether the dataset function you are using requires onReady(), see the [Velo API Reference](https://www.wix.com/velo/reference/wix-dataset).  ```javascript $w.onReady(function () { // Page onReady() function $w("#myDataset").onReady( () => { // Dataset onReady() function console.log("The dataset is ready"); }); }); ``` * If you're calling the dataset function outside the page's [onReady()](https://www.wix.com/velo/reference/$w/onready) function (for example, in an event handler), you can assume the dataset has loaded it's data already. In fact you should **not** call the dataset function from inside the dataset's onReady() function, since if the dataset has already loaded its data, the dataset onReady() function may never run, and your code may not execute. To summarize: Wrap your dataset function in the dataset's onReady() event handler only if the function requires that the dataset's data must be loaded first, and if the function is called inside the page's onReady() function. > **Note:** Your `onReady()` may be called twice when a [page renders](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md#rendering-process). This could cause your dataset function to run twice. Use the [Rendering API](https://dev.wix.com/docs/velo/apis/wix-window-frontend/rendering/introduction.md) to avoid these side effects. ### Work with the Current Dataset Item You can get and set information for the current dataset item. Examples of a current dataset item are the current item in a dynamic page, a repeater item in which a contextual event occurred (such as a button click), or the currently displayed item on a regular page with previous and next buttons. * [getCurrentItem( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitem): Gets an object representing the current item of the dataset. * [setCurrentItemIndex( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitemindex): Saves the current item in the collection, sets the current item as the item with the specified index, and updates connected page elements with the new current item's values. * [getCurrentItemIndex( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitemindex): Gets the index of the current item in the dataset (starts from 0). * [setFieldValue( )](https://www.wix.com/velo/reference/wix-dataset/dataset/setfieldvalue): Sets the value of a field in the current item. Does not save the new value in the database collection. * [setFieldValues( )](https://www.wix.com/velo/reference/wix-dataset/dataset/setfieldvalues): Sets the value of multiple fields in the current item. Does not save the new values in the database collection. * [new( )](https://www.wix.com/velo/reference/wix-dataset/dataset/new): Saves the current item and creates a new blank item immediately following the current item. ### Filter and Sort Your Dataset's Data You can apply a variety of filters to your dataset, as well as sort the dataset's data in ascending or descending order. * [setFilter( )](https://www.wix.com/velo/reference/wix-dataset/dataset/setfilter): Filters the items in the dataset using one or more [WixDataFilter](https://www.wix.com/velo/reference/wix-data/wixdatafilter) functions. * [setSort( )](https://www.wix.com/velo/reference/wix-dataset/dataset/setsort): Sorts the items in the dataset. ### Update the Dataset and Database Collection The following functions update either the dataset or database collection and synchronize the items in the collection, dataset, and page elements. * [save( )](https://www.wix.com/velo/reference/wix-dataset/dataset/save): Saves the current dataset item in the collection and updates connected page elements with the current item's new value. * [refresh( )](https://www.wix.com/velo/reference/wix-dataset/dataset/refresh): Resends collection content to the dataset and updates connected page elements. * [revert( )](https://www.wix.com/velo/reference/wix-dataset/dataset/revert): Reverts the current item to its saved state in the collection and updates connected page elements with the current item's old values. * [remove( )](https://www.wix.com/velo/reference/wix-dataset/dataset/remove): Deletes the current dataset item from the database collection and updates connected page elements with the next item's values. ### Paginate You can use dataset pagination functions to navigate dynamic pages, load more content into a repeater, get the index of the current page, and more. A dataset's page size determines how many items are initially displayed in a repeater connected to the dataset. You can then paginate through each 'page' of the dataset, loading the next batch of items into the repeater.  Here are the dataset functions available for pagination: **Size, Index & Count** * [setPageSize( )](https://www.wix.com/velo/reference/wix-dataset/dataset/setpagesize): Sets the dataset's page size. * [getPageSize( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getpagesize): Gets the dataset's page size. * [getCurrentPageIndex( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentpageindex): Gets the index of the current dataset page (starts from 1). * [getTotalCount( )](https://www.wix.com/velo/reference/wix-dataset/dataset/gettotalcount): Gets the total number of items in a dataset. If a filter is applied to the dataset, gets the number of items that match the filter's criteria. * [getTotalPageCount( )](https://www.wix.com/velo/reference/wix-dataset/dataset/gettotalpagecount): Gets the number of pages in a dataset: the number of total items divided by the page size. **Previous and Next Item** * [hasPrevious( )](https://www.wix.com/velo/reference/wix-dataset/dataset/hasprevious): Checks whether the current item is the first item in the dataset. * [previous( )](https://www.wix.com/velo/reference/wix-dataset/dataset/previous): Saves the current item and moves to the previous item in the dataset. * [getPreviousDynamicPage( )](https://www.wix.com/velo/reference/wix-dataset/dynamicdataset/getpreviousdynamicpage): Gets the URL of the previous dynamic page (dynamic datasets only). * [hasNext( )](https://www.wix.com/velo/reference/wix-dataset/dataset/hasnext): Checks whether the current item is the last item in the dataset. * [next( )](https://www.wix.com/velo/reference/wix-dataset/dataset/next): Saves the current item in the collection and moves to the next item in the dataset. * [getNextDynamicPage( )](https://www.wix.com/velo/reference/wix-dataset/dynamicdataset/getnextdynamicpage): Gets the URL of the next dynamic page (dynamic datasets only). **Previous and Next Page** * [hasPreviousPage( )](https://www.wix.com/velo/reference/wix-dataset/dataset/haspreviouspage): Checks whether the current page is the first page in the dataset. * [previousPage( )](https://www.wix.com/velo/reference/wix-dataset/dataset/previouspage): Saves the current item and moves to the previous page of items. * [hasNextPage( )](https://www.wix.com/velo/reference/wix-dataset/dataset/hasnextpage): Checks whether the current page is the last page in the dataset. * [nextPage( )](https://www.wix.com/velo/reference/wix-dataset/dataset/nextpage): Saves the current item in the collection and moves to the next page of items. **Load Content** * [loadMore( )](https://www.wix.com/velo/reference/wix-dataset/dataset/loadmore): Loads the next page of items **in addition** to the current page of items. * [loadPage( )](https://www.wix.com/velo/reference/wix-dataset/dataset/loadpage): Loads the specified page of the dataset. ### Event Handlers: Run Code Before and After Dataset Operations * [onAfterSave( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onaftersave): Perform actions right after a dataset [save( )](https://www.wix.com/velo/reference/wix-dataset/dataset/save) operation. * [onBeforeSave( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onbeforesave): Perform actions right before a dataset [save( )](https://www.wix.com/velo/reference/wix-dataset/dataset/save) operation. * [onCurrentIndexChanged( )](https://www.wix.com/velo/reference/wix-dataset/dataset/oncurrentindexchanged): Perform actions right after the current index changes. * [onError( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onerror): Perform actions right after a dataset operation causes an error. * [onItemValuesChanged( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onitemvalueschanged): Perform actions right after a value of the current item changes. * [onReady( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onready): Perform actions right after the dataset is ready. ## Only Code Sometimes you might need additional functionality for your data that isn't available with datasets. Or you might find it easier to organize your data operations by handling them only with code instead of via the UI. You can use the [wix-data APIs](https://www.wix.com/velo/reference/wix-data#top) for this purpose. The Data APIs provide you with additional functionality and customization options for managing and manipulating your site content. Here's an overview of what you can do with wix-data: > **Note:** If you're calling these functions from the page's `onReady()` event handler, be aware that `onReady()` may be called twice during the page rendering process. This can cause side effects, such as inserting or updating items, to happen twice. To avoid this, use [`wix-window-frontend.rendering.env`](https://dev.wix.com/docs/velo/apis/wix-window-frontend/rendering/env.md) to determine where your code is being executed. Learn more about [page rendering and side effects](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md#side-effects). ### Save Content to a Collection * [insert()](https://www.wix.com/velo/reference/wix-data/insert): Add a new item to a collection. Will not update an existing item. Use this function when you don't want to overwrite the item if it already exists in the collection. * [update()](https://www.wix.com/velo/reference/wix-data/update): Update an item in a collection. Will not add a new item if the item doesn't already exist. * [save()](https://www.wix.com/velo/reference/wix-data/save): Add or update an item in a collection. If the item exists, it will update. If not, the new item is added. * [bulkInsert()](https://www.wix.com/velo/reference/wix-data/bulkinsert): Add multiple items to a collection. * [bulkSave()](https://www.wix.com/velo/reference/wix-data/bulksave): Add or update multiple items in a collection. * [bulkUpdate()](https://www.wix.com/velo/reference/wix-data/bulkupdate): Update multiple items in a collection. ### Delete Content from a Collection * [remove()](https://www.wix.com/velo/reference/wix-data/remove): Remove an item from a collection. * [bulkRemove()](https://www.wix.com/velo/reference/wix-data/bulkremove): Remove multiple items from a collection. * [truncate()](https://dev.wix.com/docs/velo/apis/wix-data/truncate.md): Remove all items from a collection. ### Get Content from a Collection and Display It on Your Site * [get()](https://dev.wix.com/docs/velo/apis/wix-data/get.md): Retrieve a single item from a collection. * [query()](https://www.wix.com/velo/reference/wix-data/query): Start a process that will retrieve 1 or more items from a collection. The process is completed when you run the [find()](https://www.wix.com/velo/reference/wix-data/wixdataquery/find) function. #### Filter and Sort Your Retrieved Data * [WixDataQuery](https://www.wix.com/velo/reference/wix-data/wixdataquery) functions: Apply these to a [query](https://www.wix.com/velo/reference/wix-data/query) to filter or sort your retrieved data. #### Limit Your Retrieved Data * [limit()](https://www.wix.com/velo/reference/wix-data/wixdataquery/limit): Limit the number of items a [query](https://www.wix.com/velo/reference/wix-data/query) returns. * [skip()](https://www.wix.com/velo/reference/wix-data/wixdataquery/skip): Set the number of items to skip before returning [query](https://www.wix.com/velo/reference/wix-data/query) results. #### Get the Results of a Query Once you have added filters, a sort, or limited your query, you can get the query results: * [WixDataQuery.find( )](https://www.wix.com/velo/reference/wix-data/wixdataquery/find): Run the query and return the results. * [WixDataQueryResult.items](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/items): The returned query items. #### Paginate Your Query Results You can use the [WixDataQueryResult](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult) functions and properties to paginate your query results. * [totalCount](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/totalcount): Total number of items in the query result. * [pageSize](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/pagesize): The number of items in a query results page. Defined by the [limit()](https://www.wix.com/velo/reference/wix-data/wixdataquery/limit) function. * [length](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/length): The number of items in the current page of query results. Defined by the [limit()](https://www.wix.com/velo/reference/wix-data/wixdataquery/limit) function. Identical to **pageSize** except for the last page of results, which depends on the remaining number of items. * [totalPages](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/totalpages): Total number of pages in the query result. * [currentPage](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/currentpage): Index of the current results page. * [hasNext( )](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/hasnext): Whether there is another page of query results. * [hasPrev( )](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/hasprev): Whether there is a previous page of query results. * [next( )](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/next): Retrieves the next page of query results. * [prev( )](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/prev): Retrieves the previous page of query results. You can also paginate the [results of a referenced query](https://www.wix.com/velo/reference/wix-data/wixdataqueryreferencedresult). ### Work with Collection Reference Fields Use [reference fields](https://support.wix.com/en/article/displaying-content-from-multiple-content-collections-using-reference-fields) to connect one database collection with another database collection. * [insertReference()](https://www.wix.com/velo/reference/wix-data/insertreference): Insert a reference to an item/items. * [isReferenced()](https://www.wix.com/velo/reference/wix-data/isreferenced): Check whether a reference to an item exists. * [queryReferenced()](https://www.wix.com/velo/reference/wix-data/queryreferenced): Get full referenced items. Can be used to overcome the limitations of a standard [query()](https://www.wix.com/velo/reference/wix-data/query) with an [include()](https://www.wix.com/velo/reference/wix-data/wixdataquery/include) function. * [removeReference()](https://www.wix.com/velo/reference/wix-data/removereference): Remove a reference. * [replaceReferences()](https://www.wix.com/velo/reference/wix-data/replacereferences): Replace references with other references. * [WixDataQuery.include()](https://www.wix.com/velo/reference/wix-data/wixdataquery/include): Refines a query so that the items returned in the query's results include the full referenced items for the specified properties. * [WixDataQueryResult.partialIncludes](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/partialincludes): Whether a query that used [include()](https://www.wix.com/velo/reference/wix-data/wixdataquery/include) to add referenced items to the result returned a trimmed result due to exceeding the 50-reference item limit. You can use [queryReferenced()](https://www.wix.com/velo/reference/wix-data/queryreferenced) to overcome the limits of include(). ### Aggregate: Perform Calculations on Collection Data * [aggregate()](https://www.wix.com/velo/reference/wix-data/aggregate): Build an aggregation. After performing calculations on your aggregation, you use the [run()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/run) function to run the aggregation and return the results. * [sum()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/sum): Calculate the sum of all values of a collection field. * [avg()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/avg): Calculate the average value of a collection field. * [count()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/count): Calculate the item count of each aggregation group. * [max()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/max): Get the maximum value from each aggregation group. * [min()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/min): Get the minimum value from each aggregation group. You can [sort](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/ascending), [filter](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/filter), [group](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/group), [limit](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/limit), or [skip](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/skip) items in an aggregation. You can also [paginate](https://www.wix.com/velo/reference/wix-data/wixdataaggregateresult/next) the [results](https://www.wix.com/velo/reference/wix-data/wixdataaggregateresult) of an aggregation. ### Hooks: Perform Actions Before or After Specific Data Operations You can run any code you want via a [hook](https://www.wix.com/velo/reference/wix-data/hooks) immediately prior to or following specific data operations, such as [insert()](https://www.wix.com/velo/reference/wix-data/insert), [remove()](https://www.wix.com/velo/reference/wix-data/remove), [update()](https://www.wix.com/velo/reference/wix-data/update), and [query()](https://www.wix.com/velo/reference/wix-data/query). For example, let's say you wanted to perform a calculation on some data or capitalize some strings before you insert them into a database collection, you could use the [beforeInsert()](https://www.wix.com/velo/reference/wix-data/hooks/beforeinsert) hook. >**Notes** > • If you receive a "**Collection didn't load due to a syntax error**" message, review your **data.js** file and confirm that the syntax and formatting of your hooks are correct. > > • We recommend implementing \`afterQuery()`\ with in-memory operations only. Don't use network calls such as REST API requests or other wix-data operations, as these can lead to timeouts and issues loading collection data. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Caching Data Query Results ## Article: About Caching Data Query Results ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/about-caching-data-query-results.md ## Article Content: # About Caching Data Query Results Data caching significantly speeds up data queries. Wix automatically caches the results of certain data queries to a Content Delivery Network (CDN). When a visitor makes a query whose results are cached, Wix provides the cached results instead of querying the database again. ## When does data caching occur? Whether the results of a data query are cached depends on several factors. ### Which data query results are cached? Data query results are only cached when: - The query does not modify the collection or any of its items. - The collection permissions do not allow [anonymous site visitors to add, update, or delete items](https://support.wix.com/en/article/cms-collection-permissions-overview#use-the-show-content-permission-for-view-only-access). - The queried collection is a [native Wix Data collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection). - The query does not trigger any [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md). - The request is made by code that runs on the [site frontend](https://dev.wix.com/docs/develop-websites/articles/get-started/website-development-features.md). - The request is made on behalf of a [site visitor](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-identities.md). - The request does not contain [Personally Identifiable Information (PII)](https://support.wix.com/en/article/about-personally-identifiable-information-pii). - [Eventual consistency](#cache-invalidation-and-eventual-consistency) is not overridden. - The collection does not have [item visibility](https://support.wix.com/en/article/cms-controlling-live-site-item-visibility-from-your-collection) enabled. ### Which data request results are not cached? Results of data requests are not cached when: - The request modifies the collection or the items it contains, such as by calling [`insert()`](https://dev.wix.com/docs/velo/apis/wix-data/insert.md) or [`remove()`](https://dev.wix.com/docs/velo/apis/wix-data/remove.md). - The collection permissions [allow site visitors to add, update, or delete items](https://support.wix.com/en/article/cms-collection-permissions-overview#set-advanced-permissions-for-more-access-control). - The data request is to a [Wix app collection](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md), an [external collection](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md), or to a [shared collection](https://support.wix.com/en/article/wix-enterprise-sharing-cms-collections-with-sites-and-templates). - The request triggers a [data hook](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md). - The request is made by code that runs on the [site backend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/about-the-site-backend.md). - The data request is made on behalf of a [site member](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-identities.md). - The request contains [Personally Identifiable Information (PII)](https://support.wix.com/en/article/about-personally-identifiable-information-pii). - [Eventual consistency](#cache-invalidation-and-eventual-consistency) is overridden. - The collection has [item visibility](https://support.wix.com/en/article/cms-controlling-live-site-item-visibility-from-your-collection) enabled. ## Data cache invalidation Cache invalidation allows new, updated data to be cached and returned to visitors in future queries. The cache is automatically cleared when: - A request makes changes to the collection or to any of its items. Any cached results that reference the modified collection are also cleared. - The [site is published](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/publishing-your-site.md). - Results have been cached for one week or longer. To make the best of Wix's data caching, avoid frequent or unnecessary cache invalidations. This might require splitting some collections into two separate collections: one that contains data likely to change frequently, and another whose data is not likely to be modified often, so its associated cache is less likely to be invalidated. ### Cache invalidation and eventual consistency Since cache invalidation might take a few seconds to complete, it increases the chance that out-of-date data could be returned to site visitors. This can make the effects of [eventual consistency](https://dev.wix.com/docs/velo/apis/wix-data-v2/eventual-consistency.md) more noticeable. To ensure that query results contain the most up-to-date data, you can override [eventual consistency](https://dev.wix.com/docs/velo/apis/wix-data-v2/eventual-consistency.md). When running the data query using [`find()`](https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/find.md), [`count()`](https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/count.md), or [`distinct()`](https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/distinct.md), set the `options.consistentRead` parameter to `true`. For example: ```js import { items } from "@wix/data"; async function findItems() { const results = await items.query("myCollection").find({ consistentRead: true, }); } ``` ## Data caching and site monitoring Wix's [site monitoring dashboard](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/monitoring-your-published-site/working-with-the-monitoring-dashboard.md) allows site collaborators to view and analyze site performance. At present, user requests that are served cached results are monitored but not displayed on the dashboard. ## See also - [Wix data and eventual consistency](https://dev.wix.com/docs/velo/apis/wix-data-v2/eventual-consistency.md) - [Working with Wix app collections and code](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) - [Working with the monitoring dashboard](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/monitoring-your-published-site/working-with-the-monitoring-dashboard.md) - [About page caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-page-caching.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Indexes and Wix Data Collections ## Article: Indexes and Wix Data Collections ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/indexes-and-wix-data-collections.md ## Article Content: # Velo: Indexes and Wix Data Collections
**Important:** Additional indexes are available for sites with premium plans. If you need additional indexes, you can [upgrade your site](https://www.wix.com/upgrade/website). Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md).
Wix data collections give you the ability to add indexes to your collections. Use indexes to speed up data retrieval times and manage your data. ## What is an index? An index is a set of keys used to improve performance when searching for data in a database. Indexes work by using a lookup key to quickly locate the requested data without having to search the entire data collection. For example, in a textbook, you look in the index for the subject that you are interested in, then go directly to the page number without searching through the whole book. An index in a database works the same way. It helps the database go directly to the requested item without scanning through the whole collection. You can create a database index on a single field or on multiple fields. If a single field is not specific enough to help find your data, adding additional fields to the index can make it faster. Indexes have a sort order. When you define an index, you specify whether the index is ascending or descending, based on how you regularly access the data. A unique index is a special index that enforces the uniqueness of the field that it is defined for. If the index is a unique index, only one item in the collection can have a given value for the indexed field. ## What can I do with an index? Use indexes to speed up data retrieval and get faster response times for database reads on your site. You can also use indexes to avoid duplicate data in your database. By creating a unique index for a given field, the database rejects new or updated items that have the same value for the index field as an existing item. Indexes also speed up sorting when the fields use `.eq` filters and `.ascending` or `.descending`. Add a unique index on a data collection to prevent duplicates. While indexes greatly enhance data retrieval speeds, they can slow down the write speed slightly. The write speed for creating and updating items is a bit slower because the database has to write the index as well as the item data. Additionally, there is a small increase in the amount of data you have to store because your database is now storing the index as well as the item data. **When to add an Index** Using an index can improve your query speeds if: - You have a large data collection and your query times are slow. - You have queries that are used often and need to return data quickly. - Your queries are not slow yet, but your collection is expected to grow significantly. **When not to add an Index.** Indexes are not always necessary. You don't need to add an index in the following cases: - If you write to the collection frequently, but it's not read by the main application. For example, a log file. - If the collection is not expected to grow significantly. - If the collection already has an index. > **Note:** > All Wix collections are created with 2 indexes: > > - \_id ascending > - \_createdDate descending, \_id descending. ## Indexes and your Wix collections See [Adding an Index for Your Collection](https://support.wix.com/en/article/content-manager-adding-an-index-for-your-collection) to learn how to add an index to your Wix collections. You can create both regular and unique indexes for Wix sites: - Regular indexes can include up to 3 fields. - Unique indexes ensure that none of the items in your collection have identical values for the indexed field. Unique indexes can include only 1 field. Unique indexes also function like a regular index. The number of indexes you can add to your site depends on the [premium plan](https://www.wix.com/upgrade/website) you have. Learn more about the [data platform resources](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) available for premium plans. > **Notes:** > > - A unique index also functions like a regular index. > - Some premium plans include an additional 8 regular indexes per collection. ### Which fields make good indexes? Which indexes to create and which fields those indexes contain, is determined by the queries that your site uses. Define your indexes using the fields that you would search by, and that are commonly used in your site's queries. For example, in the product collection, if your site visitors often search for products by name, add an index on product name. The index makes querying by product name faster. ```javascript wixData.query("Products").eq("productName", "Watermelon"); ``` If your site visitors browse by category and price, add an index on`category`(ascending) and`price`(ascending). The index speeds up the querying by both fields. ```javascript wixData.query("Products").eq("category", "Fruit").eq("price", 1.39); ``` In big collections, it may not be possible to get items in order, especially when fetching by page using `.skip()`. In the example below, the database skips the first 5000 items, then performs the sort. Ordering by an index solves this problem as the database sorts first, and then skips the first 5000 items. ```javascript wixData.query("Products").skip(5000).ascending("category").ascending("price"); ``` You are not restricted to the index fields in your query. You can use other fields in the query and still get the benefit of the index. Additionally, the order that you specify the fields in your query does not matter. ```javascript wixData .query("Products") .eq("category", "Fruit") .gt("price", 1.29) .eq("origin", "Spain"); ``` ### Which fields make bad indexes? Indexes work best when the fields that they are defined for have a diverse range of values. Fields that have a narrow range of values, like booleans, do not make good indexes. If color is a common search filter, adding an index on product color makes those queries faster. If however, you only offer black or white products, the index does not result in any improvement in your query speeds. ### Working with unique indexes When adding a unique index, if the field that you are using for your index is not unique, an error occurs. Wix Data functions that write to your collection throw errors if the unique index constraint is violated. For [save()](https://www.wix.com/velo/reference/wix-data/save), [insert()](https://www.wix.com/velo/reference/wix-data/insert), and [update()](https://www.wix.com/velo/reference/wix-data/update)), the operation will fail and the collection will not be modified if the index constraint is violated. For bulk functions, [bulkInsert()](https://www.wix.com/velo/reference/wix-data/bulkinsert), [bulkSave()](https://www.wix.com/velo/reference/wix-data/bulksave), and [bulkUpdate()](https://www.wix.com/velo/reference/wix-data/bulkupdate), items that violate the unique index constraint are not modified and are added to the errors array of the response object. Items that do not violate the index constraint are updated or added to the collection. ### Supported operators Indexes improve query speeds for the following logical operators: - eq - gt - gte - lt - lte - hasSome The [filter()](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/filter) function in [WixDataAggregate](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/introduction) makes use of indexes. Indexes also speed up [distinct](https://www.wix.com/velo/reference/wix-data/wixdataquery/distinct) queries. ### Supported data types You can define indexes on all scalar data types: - Text - Number - String - Boolean Other Wix Data types that map to the above primitive types are also supported, for example, URLs. Indexes work with arrays that hold scalar data types, for example Tags. ### Using more than 1 index A collection is limited to 3 indexes. However, 3 indexes may not be enough in cases where your site visitor can choose a number of filters, and some filters may be left blank. For example, 3 fields: `name`, `phone`, and `email` can be filtered in the following combinations: 1. `name` 2. `phone` 3. `email` 4. `name, phone` 5. `name, email` 6. `phone, email` 7. `name, phone, email` There are 7 combinations but only three indexes available. In this case define 3 indexes as follows: - Index 1: `name` - Index 2: `phone` - Index 3: `email` The Wix Data engine chooses one, or combines several of the indexes to speed up the query. ### Writing queries to make use of indexes Not all queries will make the best use of your indexes, but if you write your query correctly, the performance can be greatly improved. Use the following principles when writing queries so that they make use of indexes: - If your index has multiple fields, use as many as possible. If you do not use all of the fields in the index, you must include them in the order that they were specified. For example, if an index is defined for `name`, `phone`, and `email`, filtering on `name`, or on `name` and `phone` will use the index, but filtering on `phone` and `email` will not. - To use a field for ordering, all fields defined before it in an index must appear in `.eq`  filters or ordering clauses. - To use a field in range filter (`.gt, .lt, .ge, .le`) all fields defined before it in an index must appear in `.eq` filters. - Ordering will use an index if ordering directions for the fields match index direction. The following examples show when an index defined for  `category(ascending)`and `price(ascending)`speeds up the query and when it does not. **Equality** The following query makes full use of the index: ```javascript wixData.query("Products").eq("category", "Fruit").eq("price", 1.39); ``` **Range Filters** The index speeds up range queries on `price`, when filtering by  `category:` ```javascript wixData.query("Products").eq("category", "Fruit").gt("price", 1.5); ``` The index will not speed up range queries on `price`, when filtering by another field, like `size`. In the following query, only the `category` field filter would benefit from an index. This is because in order for the filter for `price` to use the index, all fields defined before it in the index must appear in `.eq` filters, and `category` is using a `.gt` filter. ```javascript wixData.query("Products").gt("category", "C").gt("price", 1.4); ``` **Partial Index** The index will also speed up queries on a subset of the indexed fields, as long as they are used in the order that they are defined in the index. For example, the following query will be improved by the index: ```javascript wixData.query("Products").eq("category", "Fruit"); ``` The query below however, will not be faster because the `category` field is defined first in the index and must be present in the query. ```javascript wixData.query("Products").gt("price", 1.5); ``` Given the following index: `brand: ascending, name: ascending, color: ascending` The  following filters will make efficient use of the index - `eq('brand', …)` - `eq('brand', …) .eq('name', …)` - `eq('brand', …) .eq('name’, …) .eq('color',`…) **Sorting** Indexes also speed up ordering when the fields use `.eq` filters, and `.ascending` or `.descending`. This query will return faster: ```javascript wixData.query("Products").eq("category", "Fruit").ascending("price"); ``` This query will not run any faster as the `category` field filter is using `.gt` not `.eq`: ```javascript wixData.query("Products").gt("category", "Fruit").ascending("price"); ``` ## Automatic indexes For large collections with high query volumes, Wix automatically identifies slow-running queries and creates indexes to improve performance. [Automatic indexes](https://support.wix.com/en/article/cms-adding-an-index-for-your-collection#about-automatic-indexes) are created behind the scenes to optimize data retrieval without any action required from you. ## Available indexes Basic plans and most premium plans support 4 indexes, 3 regular and 1 unique. Certain tier premium plans support 15 indexes, 5 regular (up to 3 fields), 2 unique, and 8 additional regular fields (1 field). If you need additional indexes than your plan offers, you can [upgrade your site](https://www.wix.com/upgrade/website). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Wix App Collections and Code ## Article: Working with Wix App Collections and Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md ## Article Content: # Velo: Working with Wix App Collections and Code This article explains what Wix App collections are and how you can work with them. ### What Are Wix App Collections? Wix App collections are special collections that come from the Wix Apps that you add to your site. These collections appear automatically in your [Content Management System (CMS)](https://support.wix.com/en/article/about-the-content-manager-7160473) when you have [Wix Data enabled](https://support.wix.com/en/article/adding-the-content-manager-to-your-site) on your site. Each app will have different collections. For example, if you have the Wix Billing app on your site, you will see Billing collections such as Invoices and PriceQuotes grouped together in a section called "Billing." If your site has the Wix Bookings app, you will see Bookings collections such as Schedule, Services and Staff listed in a section called "Bookings." **Wix Editor:** ![Wix Editor - Wix App Collections](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b6bf6413c24855aaaee739b940dbc3dd.png) **Wix Studio:** ![Wix Studio - Wix App Collections](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a1c7611bab06f3c3c6a9c4a558e863dc.png) Click a collection to open it. ## How Do Wix App Collections Work? Wix App collections contain information about the items in your Wix App. For example, if you have Wix Stores on your site, your Products collection will contain information about the products you sell in your store. The fields (columns) in the collection provide information about each item (row), such as the item's name, stock number, price, and image. Every time you add a new app item or make changes to an existing app item in your dashboard, the changes are automatically reflected in your app collection. - Some Wix App collections are read-only (that's why the table cells are all gray), so any changes you want to make must be done in the relevant app manager. - Some Wix App collections contain fields that are read-write and can be managed from the collection itself. The table cells for those fields are white. For example, see Wix Portfolio's collections. For more information about the specific fields in each Wix App collection, see the app's section under [Wix Apps](https://dev.wix.com/docs/develop-websites/articles/wix-apps/about-apps-made-by-wix.md). ### What Can I Do with Wix App Collections? You can use the information in these collections just like you would use information in a collection you created yourself. With the data in Wix App collections you can do the following: - [Display content](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) from Wix App collections in your page elements. - [Link](https://support.wix.com/en/article/about-linking-to-a-dynamic-page) to your app's pages: You can create a customized page with elements that link to your app's built-in dynamic pages such as Stores/Product pages or Bookings/Service pages. - Create your own customized [dynamic pages](https://support.wix.com/en/article/about-dynamic-pages-6513588) based on your app collections. - Create a collection with additional information about the items in your app, and use [reference fields](https://support.wix.com/en/article/adding-a-reference-field-to-a-database-collection) or [dataset filtering](https://support.wix.com/en/article/cms-formerly-content-manager-about-filtering-and-sorting-live-site-content-with-datasets) to display the additional information on your site. - Create, update, and delete data in some regular Wix App collections. This is only applicable for fields that are read-write. > **Note:** Some Wix App collections are read-only and cannot be edited. You can also write code that references these collections to add customized features to your apps. Learn how [here](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md). See the app examples in the [Coding Examples site](https://dev.wix.com/docs/coding-examples.md), as well as the app code articles for each app. ### Wix App Collection Fields You can view information about the specific fields (columns) of a Wix App's collections in the app's section under [Wix Apps](https://dev.wix.com/docs/develop-websites/articles/wix-apps/about-apps-made-by-wix.md). Each article lists the fields in the collection, including the [field name](https://support.wix.com/en/article/about-your-database-collection-fields#field-name), [field ID](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields#field-id-velo-by-wix-only), [field type](https://support.wix.com/en/article/about-your-database-collection-fields#field-type), a brief description, whether the field can connect to data, whether it can be sorted or filtered, and whether it is read-only or read-write. > **Note:** Some Wix App collection fields are read-only and can only be changed from the relevant app manager in your Dashboard, while some are read-write and can be created and edited manually. Learn more about [Wix App Collection Fields](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/working-with-wix-app-collection-fields.md). ### Wix App Collection Permissions Wix App collections have limited [permissions](https://support.wix.com/en/article/about-collection-permissions) that cannot be changed. Each collection permission (read, create, update, delete) is assigned a [role](https://support.wix.com/en/article/about-collection-permissions#roles). You can see the collection's permission settings by clicking **View permissions** in the CMS. ![View Permissions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/54b9ea3110fca89375986360d45ea896.png) Permissions affect what your visitors can do with the collection content. #### Examples For the Stores/Products collection, anyone can read collection content, and no one can create, update, or delete collection content. This means the following: - You can display all Products collection content that can connect to data in page elements, and all visitors will see the content. - You cannot connect user input elements to your Products collection and have visitors update the collection. For the Members/PrivateMembersData collection, only [site member authors](https://support.wix.com/en/article/about-collection-permissions#roles) can read collection content, and no one can create, update, or delete collection content. This means the following: - You can display PrivateMembersData content that can connect to data in page elements, but each member will only see the content from their own member item. - You cannot connect user input elements to your PrivateMembersData collection and have visitors update the collection. For the Portfolio/Projects collection, anyone can read collection content, and only admins can create, update, or delete collection content. - You can display all Projects collection content that can connect to data in page elements, and all visitors will see the content. - You cannot connect user input elements to your Products collection and have visitors update the collection. When using code to perform data operations, you can [override collection permission restrictions](https://support.wix.com/en/article/cms-collection-permissions-overview#velo-by-wix-authorization-suppression). ### Show or Hide Wix App Collections You can hide Wix App collections, so they are not displayed anywhere in the CMS: 1. Go to [CMS](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Content%20Manager&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7B%7BmetaSiteId%7D%7D/database/) in your site's dashboard. 2. Click **More Actions** and select **Advanced Settings**. 3. Click the Wix App Collections toggle to show or hide these collections. ![Advanced Settings panel with the Wix App Collections toggle enabled.](https://wixmp-833713b177cebf373f611808.wixmp.com/images/878a430637037bc892522bfe41d2bdb9.jpeg) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Importing and Exporting Collection Data with Code ## Article: Importing and Exporting Collection Data with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/importing-and-exporting-collection-data-with-code.md ## Article Content: # Velo Tutorial: Importing and Exporting Collection Data with Code In this article we will demonstrate how to import data into a collection and export data from a collection using code. We create a simple form and then write some code that imports and exports JSON data from your collections using the `wix-data` API. > **Note:** > You can also [import](https://support.wix.com/en/article/content-manager-importing-your-content-into-a-collection) and [export](https://support.wix.com/en/article/content-manager-exporting-content-from-your-collection) collection data using the CMS. ### Prerequisites Since the `wix-data` API requires that your data be in JSON format, you'll need to be familiar with converting your data to and from JSON. > **Note:** > In this article we assume you are starting with your data in a spreadsheet or database application. **To import data into a collection:** 1. Use a spreadsheet or database application to export your data in the CSV format. 2. Use an online tool, such as [convertcsv.com](http://www.convertcsv.com/csv-to-json.htm), to convert your CSV data to the JSON format. 3. Use the form as described below to import the JSON data into your collection. **To export data from a collection:** 1. Use the form as described below to export the JSON data from your collection. 2. Use an online tool, such as [convertcsv.com](http://www.convertcsv.com/json-to-csv.htm), to convert your JSON data to the CSV format. 3. Use a spreadsheet or database application to view or import your data from the CSV format. ### Data Setup Since we are going to use the `wix-data` API to import data, your data will need to use the **Field ID**, and not the **Field Name**, to identify which fields you are using in your data source. Meaning, the first row of your CSV file should contain your collection's field IDs. To find the **Field ID** for your collection's fields, click the vertical ellipses that appears when you hover over a field in the Editor and select **Manage Properties**. If you want to provide your own IDs for the items you are adding into your collection, add a field with the key `_id` that contains the ID values. If you don't provide your own ID values, each item will get a unique, auto-generated ID value.
**Important:** The API will not check the data you are importing to ensure it matches the **Field Type** of the field you are importing the data into. Data with the wrong type will be imported successfully and an error indication will be shown in the CMS.
### Form Start with a new page in your site and create a form similar to the one shown here:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/08/10/a134b82e-129e-46c3-b6d0-7b143666be68.png)
| Type | ID | Usage | | ---------- | --------------- | ------------------------------------------------------------------------------ | | Text Input | collectionInput | For entering the collection to import to or export from | | Text Box | textBox | For entering data to be imported or for displaying data that has been exported | | Button | importButton | For triggering an import | | Button | exportButton | For triggering an export | ### Code In the **Page Code** panel (Wix Editor) or the **code editor** (Wix Studio), start by importing the `wix-data` API as the very first line of code. ```javascript import wixData from "wix-data"; ``` #### Import Code Select the Import button we created above and use the **Properties & Events** panel to add an **onClick** event handler. In the **Page Code** panel (Wix Editor) or the **code editor** (Wix Studio), add the code below to the event handler: ```javascript export function importButton_onClick(event) { const items = JSON.parse($w("#textBox").value); const collection = $w("#collectionInput").value; items.forEach((item) => { wixData .insert(collection, item) .then((results) => { console.log(`Added item: ${JSON.stringify(results)}`); }) .catch((err) => { console.log(err); }); }); $w("#textBox").value = ""; } ``` This code pulls the name of the collection and the JSON items from the form elements. It then loops through each JSON item and adds it to the collection using the `insert()` function. #### Export Code Select the Export button we created above and use the **Properties & Events** panel to add an **onClick** event handler. In the **Page Code** panel (Wix Editor) or the **code editor** (Wix Studio), add the code below to the event handler: ```javascript export function exportButton_onClick(event) { let collection = $w("#collectionInput").value; wixData .query(collection) .find() .then((results) => { $w("#textBox").value = JSON.stringify(results.items); }) .catch((err) => { console.log(err); }); } ``` This code pulls the name of the collection from the form element and uses it to query the collection using the `query()` and `find()` functions. The above code will export up to 50 items from your collection. If your collection has more than 50 items, you need to add a limit that specifies the maximum number of items that the query will return: ```javascript export function exportButton_onClick(event) { let collection = $w("#collectionInput").value; wixData .query(collection) .limit(1000) .find() .then((results) => { $w("#textBox").value = JSON.stringify(results.items); }) .catch((err) => { console.log(err); }); } ``` The maximum allowable limit is 1000. So if you want to export more than 1000 items from your collection, you will need to perform multiple queries. ### Using the Form **To import data using the form:** 1. Enter the name of the collection you want to import the data into. 2. Paste the data in JSON format into the text box. 3. Click the **Import** button. 4. As each item is imported into the collection, a success or error message is logged to the console. **To export data using the form:** 1. Enter the name of the collection you want to export the data from. 2. Click the **Export** button. 3. The exported data is displayed in the text box or an error is logged to the console. ### Additional Considerations When you [enable Sandbox for your collections](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing) importing or exporting can be performed on your [Sandbox or Live collection](https://support.wix.com/en/article/about-sandbox-and-live-data). If you import or export while you're previewing your site, the operation will be performed using your Sandbox collection. If you import or export on your published site, the operation will be performed using your Live collection.
**Warning:** Publishing your site with the form created above is a potential security risk. Either don't publish your site with the form or take measures to ensure that it is only accessible to visitors with the proper permissions.
You may need to use the form on your site only once to import or export data from the Live collection. In that case you should consider if you want to publish your site to work with the Live collection or work with the Live collection through the Sandbox collection to avoid the potential security risk described above. #### Using the form with the Live Collection without publishing your site **To export the data from your Live collection through the Sandbox:** 1. Create the form and add the code. 2. [Copy the items from your Live collection to your Sandbox collection](https://support.wix.com/en/article/cms-about-sandbox-and-live-collections-and-syncing#syncing-a-sandbox-collection-to-a-live-collection). 3. Preview your site and use the form to export your data. 4. Delete the page that contains the form before your next site publish. **To import data to your Live collection through the Sandbox:** 1. Create the form and add the code. 2. Preview your site and use the form to import your data. 3. [Copy the imported items from your Sandbox to your Live collection](https://support.wix.com/en/article/cms-about-sandbox-and-live-collections-and-syncing#syncing-a-sandbox-collection-to-a-live-collection). 4. Delete the page that contains the form before your next site publish. #### Using the form on your published site If you are going to import or export more than once and need the form for future use: 1. Create the form and add the code. 2. [Hide the form's page](https://support.wix.com/en/article/hiding-a-page-from-the-menu) so it doesn't appear in your site's menu. 3. [Password protect the form's page](https://support.wix.com/en/article/password-protecting-your-site-or-a-specific-page) so visitors can't access the page even if they have its URL. 4. [Add a Member Login bar](https://support.wix.com/en/article/site-members-adding-and-customizing-your-login-bar) if necessary (see note below). 5. Publish your site. 6. Use the form when previewing your site to import and export using your Sandbox collection. 7. Use the form on your published site to import and export using your Live collection. >**Note:** >If your site contains a Member Login button and you log into your published site, you will be assigned the Admin role, >giving you full permissions to the collections you want to import and export from. If you are not logged into the >published site, you will be assigned the Visitor role. You will only be able to import into collections where the create >permission is set to Anyone, and you will only be able to export from collections where the read permission is set to >Anyone. ### API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). **$w.TextBox** - [$w.TextBox.value](http://wix.to/94BuAAs/$w.TextBox.html#value) - Sets or gets an element's value. **$w.TextInput** - [$w.TextInput.value](http://wix.to/94BuAAs/$w.TextInput.html#value) - Sets or gets an element's value. **wix-data** - [wix-data.insert( )](http://wix.to/94BuAAs/wix-data.html#insert) - Adds an item to a collection. - [wix-data.query( )](http://wix.to/94BuAAs/wix-data.html#query) - Creates a query. - [wix-data.WixDataQuery.find( )](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#find) - Returns the items that match a query. - [wix-data.WixDataQuery.limit( )](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#limit) - Limits the number of items a query returns. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Validating User Input with Code ## Article: About Validating User Input with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/about-validating-user-input-with-code.md ## Article Content: # Velo: About Validating User Input with Code When creating a form, you may want to validate user input before it is added to your collection. Validating the data entered in your form ensures that you receive all of the data you expect and that the data is in the proper format. Whenever possible, your form validation should prevent users from entering invalid values. In cases where you can't prevent the invalid data from being entered, you should check the data before submitting it and notify the user if it's invalid. Velo allows you to set up some field-level validation using the settings of the User Input elements. Additional validation can be added using JavaScript code. This article focuses on the code-based validations. To learn more about field-level validations using the settings of the User Input elements, see [Validating User Input with Settings](https://support.wix.com/en/article/validating-user-input-with-settings).  Code-based validations allow you to add validations that are not available through the Input element settings, including validations that depend on more than one element. The functionality for code based validations can be found in `onCustomValidation()` in each supported element's [API reference](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md). See the table in [About User Input Elements](https://support.wix.com/en/article/cms-about-user-input-elements) for a list of supported elements. Note that custom validations are not run on Input elements when they don't have a value. ### Typical Validation Scenario Typically, to add custom validation you add custom validation logic in an event handler that you set using the Input element's `onCustomValidation()` function. Within that handler, you call the `reject()` function to indicate that the element is invalid. The element's validity is checked when the value of the element changes either by user interaction or programmatically. ### Example Let's take a look at a simple example. Suppose we have a form that has an email field and we want to limit users to entering email addresses with a certain domain. To do so we add the following code in the page's `onReady()` function: ```javascript $w.onReady(function () { $w("#textInput1").onCustomValidation( (value, reject) => { if( !value.endsWith("@wix.com") ) { reject("Email address must be a wix.com address."); } }); }); ``` On line 2 we call the element's `onCustomValidation()` function to register the event handler that is called when the element is being validated. That function receives two parameters, `value` and `reject`. The `value` parameter is the current value of the element that is being validated. The `reject` parameter is a function that we can call to invalidate the element. On line 3 we check to see if the `value` doesn't end with `wix.com`. On line 4, which is reached only if the `value` doesn't end with `wix.com`, we invalidate the element using the `reject()` function and pass it a rejection message. ### Additional Validation Functionality The [Velo API](https://dev.wix.com/docs/velo/api-reference/$w/introduction.md) contains some additional functionality that can be used when performing custom validations. See the table in [About User Input Elements](https://support.wix.com/en/article/cms-about-user-input-elements) for a list of supported elements. #### valid The `valid` property indicates if an element's value is valid. It takes both standard and custom validation into consideration. #### validity The `validity` property returns a `ValidityState` object that contains detailed information about why an element is invalid. #### validationMessage The `validationMessage` property returns a message indicating why an invalid element is invalid. You can set the `validationMessage` using the `reject()` function. If you don't set a custom message, the `validationMessage` may contain a standard validation message, such as `"value missing"` or `"type mismatch"`. #### resetValidityIndication() The `resetValidityIndication()` function clears the visual cue that signifies that an element is invalid. #### updateValidityIndication() The `updateValidityIndication()` function updates the visual cue that signifies whether an element is invalid based on the element's current validity state. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Processing User Input Before it is Stored in a Collection with Data Hooks ## Article: Processing User Input Before it is Stored in a Collection with Data Hooks ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/processing-user-input-before-it-is-stored-in-a-collection-with-data-hooks.md ## Article Content: # Velo Tutorial: Processing User Input Before it is Stored in a Collection with Data Hooks Sometimes you want to validate, alter, or otherwise manipulate user input that you collect using a form before it is stored in your collection. To do so, you can create a [data hook](https://support.wix.com/en/article/about-data-hooks) that intercepts newly created items before they are stored in your collection. Then you can change the item any way you like and that changed item will be what is added to your collection. To demonstrate this concept we use an example of a real estate site that has an inquiry form. Users will fill out the form to submit their information. Before the user data is saved in a collection, we will use a hook to format some of the data and add some information to the new item if certain conditions are met. ### Prerequisites This article assumes you are familiar with creating [database collections](https://support.wix.com/en/article/about-database-collections) and [user input forms](https://support.wix.com/en/article/cms-creating-a-custom-form-with-input-elements-that-submit-to-your-collection). You also might want to read more about [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) before continuing. ### Collection We begin by creating a database collection that stores the information users submit using a form. Since we are using the collection to store user input, we set its permissions using the **Form Submission** preset.  For our example, we call our collection PotentialClients and it has the following fields: * First Name - Text * Last Name - Text * Email - Text * Phone - Text * Type -Text (Value will be "Buy" or "Rent") * Bedrooms - Number * Bathrooms - Number * Price - Number * Priority - Boolean ### Input Form The next step is to create a user input form. Because a hook will be used to modify the collection item that is created when a user submits the form, the fields in the form do not need to exactly match the items in the collection.  In our example, we use the following form:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/01/16/05ee94d0-a3a4-4bd5-a820-07f3de369d98.png)
The form contains input elements that match almost all of the fields in the PotentialClients collection. Notice that there is no input element that corresponds to the Priority field. That is because the value for the Priority field will be set in the hook that gets called with a user submits the form. ### Hook The final step is to [create a hook](https://support.wix.com/en/article/how-to-use-data-hooks) that modifies the newly created item before it gets inserted into the collection. The beforeInsert hook is triggered when a new item is being inserted into a collection. The hook receives the item that is about to be inserted. In the hook's code, you can modify that item any way you like or create a totally new item. When you're done, you return the modified or new item. That returned item is inserted into the collection instead of the item that the hook received. The code for hooks is written in the **data.js** file in the **Code Files, Backend** section of your site. To learn more about creating hooks, see [How to Use Data Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md). In our example, the following beforeInsert hook does two things: * Formats the first and last names so that regardless of how users type their names they: * Start with a capital letter.  * The rest of the letters are lowercase. * Checks to see if the potential client is a priority client or not and sets the priority field accordingly. ```javascript export function PotentialClients_beforeInsert(item, context) { item.firstName = toUpperFirst(item.firstName); item.lastName = toUpperFirst(item.lastName); if(item.price > 1000000 && item.type === 'Buy') { item.priority = true; } return item; } function toUpperFirst(s) { return s.charAt(0).toUpperCase() + s.slice(1); } ``` Let's take a look at the code one piece at a time. * * * On line 1, we begin the hook function definition. Hook functions are named with the following convention: `_`. So our hook is named `PotentialClients_beforeInsert`. export function PotentialClients_beforeInsert(item, context) { * * * On lines 2-3, the hook takes the first and last names and formats them using the `toUpperFirst()` function imported above. item.firstName = toUpperFirst(item.firstName); item.lastName = toUpperFirst(item.lastName);
**Note:** You may want to create the **toUpperFirst()** function in a separate file and import it in **data.js**. That will allow you to use the function elsewhere.
* * * On lines 5-7, the hook checks the values of the `price` and `type` properties. If the potential client is looking to buy a property over $1,000,000, the item in the collection will be marked as a priority item. if(item.price > 1000000 && item.type === 'Buy') { item.priority = true; } * * * Finally, on line 9, the hook returns the modified item. That means the item inserted into the collection will have the formatted names and the priority field set correctly. return item; --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding Success and Error Messages for Elements Without a Submit Option ## Article: Adding Success and Error Messages for Elements Without a Submit Option ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/adding-success-and-error-messages-for-elements-without-a-submit-option.md ## Article Content: # Velo: Adding Success and Error Messages for Elements Without a Submit Option
Visit the Velo by Wix website to onboard and continue learning.
When you create an input form using [User Input elements](https://support.wix.com/en/article/working-with-user-input-elements-2499809), you can add customized success and error messages that are displayed only when a user tries to submit data to a collection. For any element that allows you to add a link (e.g. a button), you can automatically add success and error messages to your form in the Connect panel of the element.  What if you want to use code to trigger form submission based on some other user action besides clicking an element that has a link? For example, you may want a radio button selection to trigger the submit. As long as you connect the input elements to the same dataset as the triggering element and use any of the dataset APIs that trigger a save, the submit messages will be displayed.
**Note:** You can only add form submit messages if your connected dataset is set to either Write-only or Read & Write.
### To trigger a submit message from elements that don't have a submit option: 1. Add input elements to create your form and connect them to the same dataset. 2. Add a Text element to your page where you want the submit message to appear, modify the text for your message, and click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2017/12/05/147c47a5-dda3-44c0-a653-d373834cfe4b.png) **Connect to Data.** 3. Connect the Text element to the same dataset as the input elements for your form. 4. In the **Connection Options** section under **Form Messages**, select either **Submit Succeeded** or **Submit Failed**.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2018/01/17/f3345c6c-c976-4942-8498-953514064d0e.png)
1. Add an event handler for the event that you want to the trigger the submit. 2. Inside the event handler function call one of the APIs that saves the current dataset item. 3. Repeat steps 2 through 4 to create both messages, if needed.
**Tip:** Because success and failure messages never appear together, you can position one text element to completely overlap another on your page so the messages take up less space. Only the relevant message is displayed when a user attempts to submit data.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Creating Searches and Filters of Displayed Data ## Article: About Creating Searches and Filters of Displayed Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/about-creating-searches-and-filters-of-displayed-data.md ## Article Content: # Velo: About Creating Searches and Filters of Displayed Data To let your users search or filter the data you display on a page you start by adding User Input elements to your page. These elements are where your users will enter the criteria they want to use to search or filter your data. Then you write the code that runs the search or filter. For example, you could add code to an input box so that visitors can type their search text, or add code to a dropdown list or radio buttons to allow visitors to filter content based on their selection. It's important to remember not to connect your user input element to data when using it as input for searches and filters. To learn more about creating searches and filters via code, see the following articles: - [Velo Tutorial: Adding Collection Data Search Functionality](https://support.wix.com/en/article/how-to-add-a-search-of-your-data-collection-to-a-page) - The [wix-data API](https://www.wix.com/velo/reference/wix-data.html) - The [wix-dataset API](https://www.wix.com/velo/reference/wix-dataset/dataset.html#setFilter). You can also watch our demos on how to create searches and filters for your collection: `youtube:https://www.youtube.com/watch?v=Hx7_8-lRsW0` `youtube:https://www.youtube.com/watch?v=VbROpL-FsLs`
**Tip** You can also [use input elements to allow visitors to filter content](https://support.wix.com/en/article/cms-formerly-content-manager-setting-up-filters-for-site-visitors-using-input-elements) directly in the Wix Editor, without code.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Display Database Collection Content in a Repeater ## Article: Display Database Collection Content in a Repeater ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md ## Article Content: # Velo Tutorial: Display Database Collection Content in a Repeater This tutorial for Velo beginners explains how to display database collection content in a repeater using code. >**Note** > You can also [display database content in a repeater](https://support.wix.com/en/article/displaying-collection-content-in-a-repeater) without any code using a dataset, but using code provides you with additional functionality and options. ## Overview [Database collections](https://support.wix.com/en/article/creating-a-content-collection) are where you store your site's data. For example, if you have a real estate site, you'll store details about each property in a collection. If you have a custom form on your site, you'll store the answers to the form in a collection. You might want to display your database collection content in a [repeater](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids). Repeaters are lists of items. Each item has the same design and layout, but different content. If you have a real estate site, you can display each property and its details in a different repeater item.
To display database content in a repeater, we'll do the following: 1. Query (extract data from) the database collection. 2. Set the queried data as our repeater's data. 3. Specify which fields of the queried collection data populate each element in our repeater. ## Step 1: Query the Database Collection To query a collection, you'll need to get the collection ID: 1. Select the **Databases** tab ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/11/25/3f283226-fc27-4797-9b28-fdfe0132de6b/745eeae0-aea8-48ea-a2cb-bc1370d0466e.png) in the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-editor-working-with-the-code-sidebar.md) (Wix Editor) or [Code panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio). 2. Hover over your collection, click the **Show More** icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2021/11/25/a74aec3c-c995-4984-86b3-859dc344dd37/1f0534ae-fdab-431c-bcca-a89c83138cea.png) and select **Edit Settings**. 3. Copy the collection ID to use in your code. Once you have the collection ID you can query the collection. We'll start by importing the [`wix-data`](https://www.wix.com/velo/reference/wix-data) module, which contains the data [`query()`](https://www.wix.com/corvid/reference/wix-data/query) function. The `wix-data` APIs contain functionality for working with your database collections with code. ```javascript import wixData from 'wix-data'; ``` We run the query on our `MyCollection` database collection: ```javascript wixData.query('MyCollection') .find() .then((results) => { if (results.totalCount > 0) { console.log("Query results:", results.items); } }) .catch((error) => { console.error(error); }); ``` ### Understanding the Code * **Line 1:** Run the `query()` function on the `MyCollection` database collection. * **Line 2:** Complete the query by calling the `find()` function. * **Line 3:** Get the results of the query after the [promise](https://support.wix.com/en/article/velo-working-with-promises) resolves. * **Lines 4-5:** If there are results, print the result items to the [console](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/about-debugging-your-code.md) to view them. * **Lines 8-9:** If there's an error, print the error to the console. The query result [`items`](https://www.wix.com/velo/reference/wix-data/wixdataqueryresult/items) are an array of objects representing each item in your database collection. For example, if you have database collection fields with [field IDs](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields#field-id-velo-by-wix-only) named `title`, `price`, `image`, and `url`, you'll have properties in each object in your results object `items` array called `title`, `price`, `image`, and `url`. ```json [ { "_id": "1234", "_createdDate": "2021-05-29T08:35:52.344Z", "title": "DOWN AVENUE", "price": 500,000, "image": "wix:image://v1/68d3a9_1de7529c444b4c9eb38401f8efe0cad2.jpg/down-avenue.jpg", "url": "https://users.wixsite.com/best-real-estate-site/listings/down-avenue" }, { "_id": "5678", "_createdDate": "2021-05-25T12:48:56.572Z", "title": "QUEENS WAY", "price": 450,000, "image": "wix:image://v1/79e4b0_2ee8539d456c5c0fb48502g9fge1dbe4.jpg/queens-way.jpg", "url": "https://users.wixsite.com/best-real-estate-site/listings/queens-way" }, ... ] ``` ## Step 2: Set the Repeater's Data Setting a repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) adds new items to the repeater. We take the results of the query that we extracted from the database collection and set it as the repeater's data.  ```javascript $w('#myRepeater').data = results.items; ``` Setting a repeater's data also triggers the repeater's [onItemReady()](https://www.wix.com/corvid/reference/$w/repeater/onitemready) event handler. ## Step 3: Connect Queried Data Fields with Repeater Elements Now we need to specify which field from the queried database collection content connects to each repeater element. For example, the database field called `title` might connect to a text element in the repeater, and a database field called `url` might connect to a read more button's `link` property. We make the connection using the [`onItemReady()`](https://www.wix.com/velo/reference/$w/repeater/onitemready) function, which is used to apply the repeated item's data to the properties of the repeated elements. `onItemReady()` is an event handler that is triggered when the items of the repeater are ready to be loaded. The event handler runs for each item in the repeater. In the code below, `title`, `price`, `image`, and `url` are [field IDs](https://support.wix.com/en/article/cms-formerly-content-manager-about-your-collection-fields#field-id-velo-by-wix-only) from the `MyCollection` database collection. [`$item`](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) is a special selector which selects elements in a specific repeater item. The `onItemReady()` function loops through each item in the repeater and connects the repeater elements in each item with the queried data. ```javascript $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#myTitleText").text = itemData.title; $item("#myPriceText").text = itemData.price; $item("#myImage").src = itemData.image; $item("#myButton").link = itemData.url; }); ``` >**Note** > The code for onItemReady() must be located before the code for setting the repeater's data. After applying the code to your page, you'll see your database content displayed in your repeater. ## Optional: Filter and Sort the Displayed Content As an additional step, you can optionally chain [`WixDataQuery`](https://www.wix.com/velo/reference/wix-data/wixdataquery) functions to your query to filter, sort, and limit the results of the query. For example, we filtered the items so that only properties whose price is less than $500,000 appear in the repeater. We also sorted the items in alphabetical order according to the property title. To do this we chained a 'less than' [`lt()`](https://www.wix.com/velo/reference/wix-data/wixdataquery/lt) filter and a [`descending()`](https://www.wix.com/velo/reference/wix-data/wixdataquery/descending) sort to the query, before executing the query with the `find()` function: ```javascript wixData.query('MyCollection') .lt('price', '500000') .descending('title') .find() .then((results) => { if (results.totalCount > 0) { $w('#myRepeater').data = results.items; } }); ``` ## Complete Code Here is the complete code for the example: ```javascript import wixData from 'wix-data'; $w.onReady(function () { $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#myTitleText").text = itemData.title; $item("#myPriceText").text = itemData.price; $item("#myImage").src = itemData.image; $item("#myButton").link = itemData.url; }); wixData.query("MyCollection") .lt('price', '500000') .descending('title') .find() .then((results) => { if (results.totalCount > 0) { $w("#myRepeater").data = results.items; } }) .catch((error) => { console.error(error); }); }); ``` ## Learn More * [Working with database collections and code](https://support.wix.com/en/article/velo-overview-of-the-wix-data-and-wix-dataset-apis) * [Wix Data APIs](https://www.wix.com/velo/reference/) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Calculating and Displaying Collection Data ## Article: Calculating and Displaying Collection Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/calculating-and-displaying-collection-data.md ## Article Content: # Velo Tutorial: Calculating and Displaying Collection Data
Visit the Velo by Wix website to onboard and continue learning.
There are many instances when you want to display data that does not come directly from one of your collections. Instead, you want to calculate values based on the data stored in a collection and display those calculated values. Sometimes you want to calculate data for each item you're displaying and other times you want to calculate aggregated values based on a number of items. In this article, we demonstrate how to display such data using a variety of different page elements. ### Per-Item Calculations There are several ways to calculate and display data on a per-item basis. Here, we will explain how to do so using a [data hook](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md). To generate a template for a hook using the sidebar: 1. Find your collection in the **CMS** section of the sidebar and mouse over it. 2. Click the Show More ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/37ff9473-4cf0-4d6b-845e-d93462da44eb/2c4c1bdd-afc8-407e-a434-7e47492e2ba6.png) icon and select **Add data hooks**.  3. Select the hooks you want to create, then click **Add & Edit Code**. Templates for each of the hooks you choose are automatically generated and added to a file named **data.js** in your site's backend code. The idea is to create an **afterQuery** hook that intercepts the data from your collection before it reaches your page code. The hook runs for each item that is retrieved by a dataset or query. The hook performs the desired calculations and adds the calculated data to each item before it gets to the page. Then, in your page code, you can get the calculated data and apply it to your page's elements.  For example, let's say you have a collection named **Employees** that contains information about employees. The collection might store an employee's first name and last name in separate fields. It also might store an employee's date of birth. But for display purposes, you want to show employee names together in one element and employee ages instead of dates of birth. To get started, you write an **afterQuery** hook for the **Employees** collection: ```javascript // In file backend/data.js export function Employees_afterQuery(item, context) { item.fullName = item.lastName + ", " + item.firstName; item.age = ageFromDob(item.dob); return item; } ``` The hook receives the item from the collection and adds two new properties to it. First, it adds a `fullName` property that concatenates the employee's name in a "lastName, firstName" format. Second, it adds an `age` property that calculates an employee's age based on the date stored in the `dob` field. The hook uses the following function, which can also be included in the data.js file: ```javascript function ageFromDob(dob) { const now = new Date(); const days = now.getDate() - dob.getDate(); const months = now.getMonth() - dob.getMonth(); let age = now.getFullYear() - dob.getFullYear(); if (months < 0 || (months === 0 && days < 0)) { age--; } return age.toString(); } ```
**Note:** You may want to create the **ageFromDob** function in a separate file and import it in **data.js**. That will allow you to use the function elsewhere.
A page that retrieves items from the **Employees** collection using a dataset or query will now have the new properties that you've added to each item. The next step is to apply the values from those properties to the page's elements. In the following examples, we will assume that the page receives its data using a dataset. Generally, this means that you can connect your page elements to any non-calculated data as you would normally do. However, since your calculated data is not part of the database [schema](https://support.wix.com/en/article/about-database-collection-schemas), you will have to "connect" the data to page elements using code. If you use a query instead of a dataset to retrieve your page's information, you can use a similar approach to apply the calculated data to your page elements. #### Text Elements To apply calculated data to a text element, you set the value of the text element's `text` property to the value of one of the item's calculated properties in the dataset's `onReady()` function. If your page's dataset contains more than one item and you allow your site visitors to click through multiple items using previous and next buttons, you also need to set the value of the text element's `text` property in the dataset's `onCurrentIndexChanged` function. You can connect all the other elements on your page to their corresponding non-calculated fields as you would normally do. For example, assuming the employee data mentioned above, you connect non-calculated fields, such as address and job title, to page elements using their connect panels. However, you leave the elements that will show the calculated data, the employee name and age, unconnected. Those connections are dealt with in the following code. ```javascript $w.onReady(function () { $w("#dataset1").onReady(() => { populateCalculatedFields(); } ); $w("#dataset1").onCurrentIndexChanged( (index) => { populateCalculatedFields(); } ); } ); function populateCalculatedFields() { const currentItem = $w("#dataset1").getCurrentItem(); $w("#textFullName").text = currentItem.fullName; $w("#textAge").text = currentItem.age; } ``` Here, the `populateCalculatedFields()` function takes the calculated data from the dataset and applies it to the corresponding page elements. It is called when the dataset is first ready and then every time the current item is changed. #### Table To apply calculated data to a table, you reset the table's columns using its `columns` property to include columns for each item's calculated values. You then set the data of the table using the table's `rows` property. You begin by connecting the table to a dataset as you would normally do. Then, use the table's **Manage Table** panel to delete any columns that you don't want to be shown in your table because you're going to calculate their data. For example, assuming the employee data mentioned above, after you connect your table to the dataset, you will delete the **First Name**, **Last, Name**, and **DOB** columns. The columns that are remaining will receive their data from the dataset. The columns that will display your calculated data are created in the following code. ```javascript $w.onReady(function () { const currentColumns = $w("#employeeTable").columns; const calculatedColumns = [ { "id": "colFullName", "dataPath": "fullName", "label": "Name", "type": "string", }, { "id": "colAge", "dataPath": "age", "label": "Age", "type": "string", } ]; $w("#employeeTable").columns = calculatedColumns.concat(currentColumns); ``` Here, the code begins by getting the current columns from the table using the `columns` property. Remember, those will correspond to the fields from the dataset, minus the columns we deleted. Next, it creates an array of two new columns that will hold the calculated data. Then, it puts both sets of columns together and uses that to reset the table's `columns` property. #### Repeater To apply calculated data to a repeater, you set the values of the corresponding elements using the repeater's `onItemReady()` function. You can connect all the other elements in your repeater to their corresponding non-calculated fields as you would normally do. For example, assuming the employee data mentioned above, you connect non-calculated fields, such as address and job title, to elements in your repeater using their connect panels. However, you leave the elements that will show the calculated data, the employee name and age, unconnected. Those connections are dealt with in the following code. ```javascript $w.onReady(function () { $w("#employeeRepeater").onItemReady( ($item, itemData, index) => { $item("#repeaterFullName").text = itemData.fullName; $item("#repeaterAge").text = itemData.age; } ); } ); ``` Here, the repeater `onItemReady()` function takes the calculated data from the dataset and applies it to the corresponding elements in the repeater items. It is called for each item in the repeater as they are created. ### Aggregations If you want to display aggregated values, using a data hook is not necessarily the best option since hooks work on a per item basis. Instead, it is usually better to perform your aggregation calculations in your page code. 
**Note:** This example only works if there are 1000 items or less in your collection. That is because the **query()** function is limited to retrieving at most 1000 items. If you have more than 1000 items, you can work around the limitation by performing multiple queries.
For example, let's say you have a collection named **Expenses** that contains information about monthly expenses. The collection might store the month, type, and amount of each individual expense. There can be multiple items for each month. Instead of displaying all the expense items individually, you want to just display a total for each month. We can use the following code to populate a table with the aggregated expense data: ```javascript import wixData from 'wix-data'; import {monthSort} from 'public/utils'; $w.onReady(function () { $w("#aggTable").columns = [ { "id": "col1", "dataPath": "month", "label": "Month", "type": "string" }, { "id": "col2", "dataPath": "total", "label": "Total Expenses", "visible": true, "type": "number" } ]; wixData.query("Expenses") .ascending("month") .limit(1000) // include a limit if you have more than 50 items .find() .then( (result) => { const months = result.items.map(x => x.month) .filter((obj, index, self) => index === self.indexOf(obj)) .sort(monthSort); const aggregated = months.map(x => { return { month: x, total: result.items.filter(obj => obj.month === x) .map(z => z.amount) .reduce((sum, current) => sum + current) }; } ); $w("#aggTable").rows = aggregated; } ); } ); ``` The code begins by setting the columns of a table. There will be two columns, one for the name of the month and another for the total expenses during that month. Next, the code performs a query on the expenses collection. Once it receives the results of the query, it uses standard JavaScript array functions to calculate the total expenditures for each month. In this particular case, the code first extracts a sorted list of the months found in the collection. It uses the following function from a public file to sort the months list: ```javascript const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; export function monthSort(a, b) { return months.indexOf(a) - months.indexOf(b); } ``` Then, for each of those months, it creates an object with two key:value pairs. The first pair corresponds to the name of the month. The second pair corresponds to the total expenditures for that month. Finally, the objects that were just created are set to be the row data for the table. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding Collection Data Search Functionality ## Article: Adding Collection Data Search Functionality ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/adding-collection-data-search-functionality.md ## Article Content: # Velo Tutorial: Adding Collection Data Search Functionality
Visit the Velo by Wix website to onboard and continue learning.
This article assumes some understanding of [database collections](https://support.wix.com/en/article/about-database-collections) and [datasets](https://support.wix.com/en/article/about-datasets-6368396) and how they are used in Velo.  With Velo you can add a database collection to your site and display the data in many ways. Visitors to your site can also search that data. You can add this search functionality to your site by adding user input elements to the page and then adding some code to enable the search. There are different ways you can let your visitors search your collection. This article covers the following options: * Adding an **input field** where visitors enter a search string. * Adding a **dropdown list** where visitors select a value from a list. While the results can be displayed however you want them to appear on the page, in this article we'll cover how to display the data in a table. At the end of the tutorial we'll also cover how to optionally collapse the table until you are ready to display the results. ## Search Using an Input Element Let's say you have a list of recipes in your collection and you want users to be able to search for them by entering their name in a user input element. Once the visitor clicks a search button, the results are displayed in a table on the same page. To do this, you add to your page an input element where the user enters a search term, a button that enables the search, and a table to display the results. Then you add code to make the table only display data that matches the user input. In this example, the table is not connected to a dataset, so code is needed to set up the table columns to match the data structure. #### 1\. Add the input, button, and table elements to the page 1. Add an input box, a button, and a table to your page.  You can customize them as you like. For example, you could change the text of the button to "Search" and configure the placeholder text of the input box to read "Search recipes." 2. Consider changing the IDs of each element that you just added to make them meaningful.  #### 2\. Add the code for inputting a search string Now configure the search button so that when it's clicked, the search is performed on the collection based on what a user entered into the input box. Then you need to have those results populate the table you added. 1. Add the import statement for the Wix Data API to the top of your code: ```javascript import wixData from "wix-data"; ``` 2. Add an [onClick event](https://www.wix.com/velo/reference/$w.Button.html#onClick) to the button. The following code is added to your page. ```javascript export function searchButton_click(event) { //Add your code for this event here: } ``` 3. Replace existing text with the following code: ```javascript // Runs a query on the "recipes" collection wixData.query("recipes") // Query the collection for any items whose "Name" field contains // the value the user entered in the input element .contains("name", $w("#searchBox").value) .find() // Run the query .then(res => { // Set the table data to be the results of the query $w("#resultsTable").rows = res.items; }); ``` #### 3\. Define the table columns Because your table isn't connected to a dataset, you need to define the columns using the [API for tables](https://www.wix.com/velo/reference/$w.Table.html#columns). You do this by defining a JSON object that lists the properties for each column and their values. The code is placed in the onReady function and looks like this: ```javascript $w.onReady(function () { $w("#resultsTable").columns = [ { "id": "col1", // ID of the column for code purposes // The field ID in the collection whose data this column displays "dataPath": "field1", "label": "Field 1", // The column header "width": 100, // Column width "type": "string", // Data type for the column // Path for the column if it contains a link "linkPath": "link-field-or-property" }, { "id": "col2", "dataPath": "field2", "label": "Field 2", "visible": true, "type": "image", "linkPath": "link-field-or-property" } //, // more column objects here if necessary // ... // ... }]; }); ``` For example, the column definitions of a table that shows the results of a search on your recipes collection might look like this: ```javascript $w.onReady(function () { $w("#resultsTable").columns = [{ "id": "col1", "dataPath": "title", "label": "Recipe", "type": "string", }, { "id": "col2", "dataPath": "course", "label": "Course", "type": "string", }, { "id": "col3", "dataPath": "meal", "label": "Meal", "type": "string", }]; }); ``` You can copy all this code directly into your page code. You need to replace the following element IDs and the collection name with those in your site. Just hover over the element to see its ID. * `wixData.query("recipes")` - Replace `recipes` with the name of your collection. * `.contains("name", $w("#searchBox")` - Replace `name` with the [field ID](https://support.wix.com/en/article/about-database-collections#field-id-velo-by-wix-only) of the field in the collection that is being searched. Replace `searchBox` with the element ID of your input box where visitors enter the string to search. * `$w("#resultsTable")` - Replace `resultsTable` with the element ID of your table that displays the search results. * `"dataPath value"` - Replace with the field IDs in your collection. You can now preview your page and check that everything works as expected.  ## Filter Using a Dropdown Element Now let's say you want users to select a value from a dropdown list of courses so they can filter your recipes. The list of courses is already in your collection. First, we add the dropdown and connect it to the collection via a dataset to populate the options directly from the collection. Then we add a table to display the selection, and finally we write the code. 
**Tip** You can also [filter displayed data based on a user selection in a dropdown](https://support.wix.com/en/article/cms-formerly-content-manager-setting-up-filters-for-site-visitors-using-input-elements) directly in the editor, without code.
#### 1\. Add the dropdown and table elements to the page 1. Add a dropdown list element and a [dataset](https://support.wix.com/en/article/adding-and-setting-up-a-dataset) to your page. Make sure the dataset is set to **Read** mode or **Read & Write** mode. 2. In the [Connect Dropdown](https://support.wix.com/en/article/cms-connecting-elements-to-a-collection-using-a-dataset) panel, select **Filter content**. 3. Add a table element to your page. #### 2\. Add the code for the dropdown selection to filter the collection Now add the code so that the user's selection in the dropdown is used to filter your collection. 1. Add the import statement for the Wix Data API to the top of your code: ```javascript import wixData from "wix-data"; ``` 2. Add an [onChange event](https://www.wix.com/velo/reference/$w.Dropdown.html#onChange) to the dropdown list. The following code is added to your page. ```javascript export function searchList_change(event) { //Add your code for this event here: } ``` 3. Replace existing text with the following code: ```javascript // Runs a query on the "recipes" collection wixData.query("recipes") // Query the collection for any items whose "Name" field contains // the value the user selected in the dropdown .contains("course", $w("#myDropdown").value) .find() // Run the query .then(res => { // Set the table data to be the results of the query $w("#resultsTable").rows = res.items; }); ``` #### 3\. Define the table columns Follow the instructions in the previous example to define the results table. Then preview your page and check that everything is working as expected. ## Filter Dropdown Options Distinctly In the last step, we displayed all recipes for a selected course in a table on the page. We might have many recipes for each course in our collection, so our table displays the course multiple times, once for each recipe.  We do not, however, want to display duplicate course values in the dropdown's options. We want each dropdown option to be distinct from the others.  First, we modify the dropdown we already added by disconnecting it from the collection in the editor. We then add code to populate the options directly from the collection.   #### 1\. Disconnect the dropdown from the collection We will populate the dropdown from the collection but with code. So let's disconnect it from the collection in the editor. 1. **Select** the dropdown list element on your page.  2. In the [Connect Dropdown](https://support.wix.com/en/article/cms-connecting-elements-to-a-collection-using-a-dataset) panel, under the **Choose a dataset** section, select the option **Not connected**.  #### 2\. Add code to populate the dropdown with distinct options We now add a function to populate our dropdown using [`distinct`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#distinct) function. We call this function `loadOptions()`. The `distinct()` function queries the collection and returns field values that do not contain duplicates. To populate the dropdown, the distinct query results have to be formatted as an array of strings in a certain format. `loadOptions()` calls the `buildOptions()` function for this purpose. This example also demonstrates how to insert an extra **All Courses** option to our dropdown so users can reset the filter from the dropdown. You can copy the following code for these functions directly into your page code. You need to replace the element IDs and the collection name with those in your site. ```javascript function loadOptions() { // Run a query that returns distinct items in the collection wixData.query("recipes") // Set the course as the field that must be distinct .distinct("course") .then(results => { // Call another function to reformat the distinct items // the way the dropdown element expects let distinctList = buildOptions(results.items); // Use `unshift()` to add another dropdown option at // the beginning of the array, in the correct format distinctList.unshift({ "value": '', "label": 'All Continents' }); // Set the options of the dropdown $w("#myDropdown").options = distinctList; }); } ``` Let's now code the `loadOptions()` function. This function takes each distinct item from the collection and reformats the option in the [format expected by the dropdown](https://www.wix.com/velo/reference/$w/dropdown/options): `{ "label": "uniqueTitle", "value": "uniqueTitle" }` ```javascript function buildOptions(dropdownItems) { return items.map(currentItem => { return { "label": "currentItem", "value": "currentItem" }; }); } ``` Now call the `loadOptions()` function in the `$w.onReady()`, to load the options into the dropdown. Replace `resultsTable` with the ID of your table. ```javascript $w.onReady(() => { loadOptions(); $w("#resultsTable").columns = [{ ... ... ... }]; }); ``` Preview your page and check that everything works as expected.  ## Optionally Collapse the Table on Load The way both searches are set up, an empty table appears when the page loads. You can collapse the table so that it does not take up any space on the page and configure it to expand only after the search is performed. 1. In the [Properties & Events panel](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/properties-events-panel/about-the-properties-events-panel.md) for the table, select **Collapsed on load**. 2. In the code you added for the search, right below where you instructed the table to be populated with the results of the query on the data collection, add this line to expand the table.  ```javascript $w("#resultsTable").expand(); ``` Replace `resultsTable` with the ID of your table. For example, the code for performing the search with a dropdown would look like this for a table whose index key is `resultsTable`: ```javascript wixData.query("recipes") .contains("name", $w("#searchList").value) .find() .then(res => { $w("#resultsTable").rows = res.items; $w("#resultsTable").expand(); }); } ``` ## API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). **wix-data** * [wix-data.query( )](http://wix.to/94BuAAs/wix-data.html#query) - Creates a query. * [wix-data.WixDataQuery.find( )](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#find) - Returns the items that match a query. * [wix-data.WixDataQuery.distinct( )](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#distinct) - Returns the distinct items that match a query. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Querying Items that Reference Other Items ## Article: Querying Items that Reference Other Items ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/reference-fields/querying-items-that-reference-other-items.md ## Article Content: # Velo: Querying Items that Reference Other Items Some collections contain items that reference items that are stored in a different collection. For example, a **Books** collection may contain a **Writer** field, which references an **Authors** collection. Using Velo, you can retrieve referenced items' details using different techniques. This article explains how.
In this example, a book is the "item" and the author is the "referenced item." ### Retrieving Referenced Item IDs Using Velo, when you query an item that references other items, by default you receive the IDs of the referenced items in the query's results. Let's query our Books collection for novels. For each novel, we want to see the referenced author. Consider the following code that runs the query and displays the results in the table we created above. ```javascript function theRegularFindExample() { // Query the Books collection for novels only wixData .query("Books") .eq("genre", "Novel") .find() .then((myResults) => { // Set up table columns $w("#table1").columns = [ { id: "col1", dataPath: "title", label: "Book", width: 100, visible: true, type: "string", }, { id: "col2", dataPath: "genre", label: "Genre", width: 100, visible: true, type: "string", }, { id: "col3", dataPath: "writer", label: "Author", width: 200, visible: true, type: "richText", linkPath: "doc", }, ]; // Display the query results in the table $w("#table1").rows = myResults.items; }); } ``` We see that the **Author** field contains ID values. We can't see the author's name, nationality, and so on.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2019/09/25/de82a016-eea8-4612-a11c-21a81c9a5e7c/4edb219a-7571-4fcb-aa78-cf05b17d680b.png)
To see the referenced information in addition to the IDs, there are other Velo functions we can use: [`queryReferenced()`](https://www.wix.com/velo/reference/wix-data.html#queryReferenced) and [`include()`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#include). At the end of this article, we describe [when to use each of these functions](#when-to-use-each-function). ### Retrieving Referenced Items with queryReferenced() Use the [`queryReferenced()`](https://www.wix.com/velo/reference/wix-data.html#queryReferenced) function to include referenced items for the specified properties in your query's results. For example, we want to see novels in our Books collections along with the author's name and nationality. The author's name and nationality exist in an Authors collection. #### Example Let's query our Books collection again for novels, but this time we want to see the author's name and nationality. First, let's look at the following code that sets up our table. We're going to take this opportunity to demonstrate how to set up table columns for an object, because we won't be loading rows directly from a collection. We'll add rows to the table using an object that we create instead. Now let's look at code that runs the query and displays the both the results and the referenced results in our table: Now we see the **Author** and **Nationality** field values.
### Retrieving Referenced Items with include() Add the [`include()`](https://www.wix.com/velo/reference/wix-data.WixDataQuery.html#include) function to your query chain to include referenced items in your query's results. This function is a bit simpler to use than `queryReferenced()`. But it does have limitations. Review the [comparison](#when-to-use-each-function) between `queryReferenced()` and `include()` to decide which function to use. When setting up our columns, we use the period  **.**  notation to access the nested, referenced items. Because the referenced items are in a different collection, we prefix their names with the name of the referencing property from the first collection. #### Example Let's query our Books collection again for novels. Again, we want to see the author's name and nationality. This time we will demonstrate how to use `include()` instead of `queryReferenced()`. Consider the following code that runs the query and displays the results in a table: ```javascript async function theIncludeExample() { wixData .query("Books") .eq("genre", "Novel") .include("writer") .find() .then((myResults) => { if (myResults.items.length > 0) { let books = myResults.items; $w("#table2").rows = books; } else { console.log("No results found."); } }); } ``` Now we see the **Author** and **Nationality** field values.
See how `include()` is used in a live demo: `youtube:https://www.youtube.com/watch?v=bGfTcp5hATM` ### When to Use Each Function This table can help you decide when to use `queryReferenced()` and `include()`. | Functionality / Limitation | queryReferenced() | include() | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | --------- | | **Can reference multiple items in one query**

If you want to see many items and their referenced data in one query, it's easier to use **include()**. | No | Yes | | **Can reference one item that references multiple items in one query** | Yes | No | | **Can reference multiple items that reference multiple items in one query** | No | No | | **Can trigger hooks** | No | Yes | | **Can reference more than 50 items**

Use **queryReferenced()** if you anticipate more than 50 items as a result of your query, even if you use the **limit()** function. | Yes | No | | **Can list up to 50 referenced items**

Use **queryReferenced( )** if you anticipate more than 50 referenced items will be returned as a result of your query. | Yes | No | | **Works with promises**

Working with promises lets you catch errors and send alerts. | Yes | No | ## See also - [Webinar: Planning for scale: large data design considerations](https://youtu.be/BGnpQ_LTHPg?feature=shared&t=1275) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Data Hooks ## Article: About Data Hooks ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md ## Article Content: # About Data Hooks
This feature is not yet supported in [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md).
Data hooks run code before or after certain interactions with your site's collections. A data hook allows you to intercept the interaction immediately before or immediately after it occurs. The hook's code can even be used to affect the interaction itself. For example, you may want to intercept an item before it is added to your collection to perform a final validation or to tweak the data that actually makes it into the collection. > **Note**: Data hooks run on the server. This means that any logs your code produces do not appear in the Developer Console in [Preview mode](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site). Instead, use the built-in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) tool to see your logs. In general, hooks are run whether the interaction with your collection is initiated by a page element or programmatically using the [Data API](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md). However, a Data API call from the backend code of your site may pass the optional `WixDataOptions` object and use it to suppress hooks from being called on that particular interaction. See [Using Data Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md) to find out how to register hooks on your collections. ### Hook Types There are a number of different points in the lifecycle of a data interaction that you can hook into. Different hooks receive different arguments and return different values. For more information, see the [Data API reference](http://wix.to/94BuAAs/wix-data.html). - `item`: The current item. For example, in `beforeInsert`, this is the item that is about to be inserted. If there are many items, such as can happen with `afterQuery` for example, the hook function will be called repeatedly, once for each of the items. - `itemId`: The current item's ID value. - `query`: The `WixDataQuery` object that will be executed. - `count`: The number of items in the count. - `error`: An error object. | Hook Name | Receives | | ------------ | -------- | | beforeInsert | item | | afterInsert | item | | beforeUpdate | item | | afterUpdate | item | | beforeRemove | itemId | | afterRemove | item | | beforeGet | itemId | | afterGet | item | | beforeQuery | query | | afterQuery | item | | beforeCount | query | | afterCount | count | | onFailure | error | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Using Data Hooks ## Article: Using Data Hooks ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/using-data-hooks.md ## Article Content: # Velo: Using Data Hooks
You may want to read [About Data Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) before following these instructions.
**Note**: Data hooks run on the server. This means that any logs your code produces do not appear in the Developer Console in [Preview mode](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site). Rather, use the built-in [Wix Logs](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/logs/view-logs-using-wix-logs.md) tool to see your logs. The code for data hooks is stored in a file called **data.js** in the **Backend** section of your site. You can add this code to the file by writing it yourself, or generate template code using the [Code sidebar](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md#built-in-code-panel). To generate a template for a hook using the sidebar: 1. Hover over the collection name in the **Databases** section of the sidebar. 2. Click the Show More ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/27/37ff9473-4cf0-4d6b-845e-d93462da44eb/2c4c1bdd-afc8-407e-a434-7e47492e2ba6.png) icon and select **Add/Remove Hooks**. 3. Choose which hooks you want to create, and click **Add & Edit Code**. Templates for each of the hooks you choose are automatically generated. The code that registers a hook follows the following format: ```javascript export function _(, context) { //hook code goes here } ``` The `collectionName` is the name of the collection that the hook will be registered to. The `hookName` is the [type of hook](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md). The hook function takes two parameters. The first parameter is dependent on which hook was called. It can be either the current item, the current item's ID, the query, count, or error. The second parameter is an object that contains contextual information about the hook, such as the name of the collection the hook affects, the current user's ID, and the permissions role of the current user. The hook function is expected to return a specific type. If it returns a value of a different type, that value is ignored. For full details on parameters and expected return values for the different types of hooks, see the [Data API reference](http://wix.to/94BuAAs/wix-data.html). A hook on an interaction that affects multiple items in the collection will be called repeatedly, once for each of the items. > **Notes:** > > - The specialized functions for modifying reference fields, such as [insertReference()](https://www.wix.com/velo/reference/wix-data/insertreference), do not trigger hook functions. > - If you receive a "**Collection didn't load due to a syntax error**" message, review your **data.js** file and confirm that the syntax and formatting is correct. Also see the note below in **"After" Hooks**. > - Certain module export formats are not supported in `data.js`. For more information, see [Module Export Syntax](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/module-export-syntax.md). In general terms, hooks work in the following manner: 1. An interaction with a collection occurs. 2. The hook function is called, receiving whatever is interacting with the collection. 3. The function performs whatever logic it wishes to, including possibly modifying whatever is interacting with the collection. 4. The function returns a version of what it has received. For example, in the following hook: 1. An item is being inserted into the collection. 2. We intercept the item before it is inserted. 3. We change the value of the item's title field to be all uppercase letters. 4. We return the changed item. ```javascript export function collection_beforeInsert(item, context) { item.title = item.title.toUpperCase(); return item; } ``` If we look at the item inserted into the collection, we'll see that its title is in all uppercase letters. Watch a demo of how to use data hooks in your code: `youtube:https://www.youtube.com/watch?v=3IHukvehUto` ### Example Data Hook Uses #### Validating Data Before Inserting You can use hooks to prevent the addition of an item to a collection if it doesn't meet certain requirements. The following example uses the `beforeInsert()` hook to validate an email address field. If the email is invalid, the function returns `Promise.reject()` and the item is not added to the collection. ```javascript export function MyCollection_beforeInsert(item, context) { const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; if (emailRegex.test(item.email)) { return item; } else { return Promise.reject("Invalid email address."); } } ``` #### "After" Hooks Hooks that trigger after a function has executed do not affect the item in the collection. They can only affect the item returned by the function, which is a copy of the item in the collection. > **Note** > Do not use the \`afterQuery()\` hook to modify collection data. This can cause the Content Management System (CMS) to fail when loading. If you need to modify the results of queries before performing other data operations use the appropriate hook, such as [\`beforeInsert()\`](https://www.wix.com/velo/reference/wix-data/hooks/beforeinsert) or [\`beforeUpdate()\`](https://www.wix.com/velo/reference/wix-data/hooks/beforeupdate). ### API List The following API is used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). - [wix-data.Hooks](https://www.wix.com/velo/reference/wix-data.Hooks.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Data Hooks for Dynamic Pages ## Article: About Data Hooks for Dynamic Pages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks-for-dynamic-pages.md ## Article Content: # Velo: About Data Hooks for Dynamic Pages When a request comes in for one of your dynamic pages, [a router uses the URL of the request](https://support.wix.com/en/article/cms-about-dynamic-pages#dynamic-item-pages) to decide which page to show and what data to bind to the page's dataset. You can add a data binding router hook to intercept this process at certain points and insert additional logic. Some hooks can be used with [router pages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) as well. **Note:** If you duplicate a dynamic page and change a router prefix, you must also update the hook function. ## Router Hooks After defining a router for your site, you can define data binding router hooks in **routers.js** located in the Public & Backend section of the Code sidebar (Wix Studio), or the Code sidebar (Wix Editor). **Note:** To define data hooks, you must first create a router and add it your site. See how to create a router in [Wix Studio](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md), or in the [Wix Editor](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md). The hook functions are named with the following convention: _() The router prefix is the first part of the URL you chose when creating your dynamic page. You can find the URL of your dynamic page in the page's settings.  For example, if you have a page with the URL `/dishes/name` and you want to create a `beforeRouter` hook, the function would look like this: export function dishes_beforeRouter(request) { // function code } The hooks you can register are listed here in the order they run: * beforeRouter * customizeQuery * afterRouter To learn more about the hook functions, see the [Router API reference](http://wix.to/94BuAAs/wix-router.html). You can also register [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) that run code before or after certain interactions with your site's collections. If you’ve done so, the data hooks will run between `customizeQuery()` and `afterRouter()`. ### beforeRouter() This hook is triggered before the router goes to the requested page. You can use this hook to route requests to a different page or return an error response. For example, you can check who is requesting the page and then decide based on the user's role whether to let the router continue to the next step or to return an error type response code. ### customizeQuery() This hook is triggered before the page's data query is executed. You can use this hook to further refine or change the query that will determine what data is bound to your page's dataset. For example, you can filter the query to only return items that have a status field set to active. ### afterRouter() This hook is triggered after the router has bound the data, but before the page is displayed. You can use this hook to change the router's response based on the data that was retrieved. For example, you can have two versions of a page, one for portrait oriented images and another for landscape oriented ones. After the image is pulled from the database, you can show the page that corresponds to the image's orientation. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating Data Hooks for Dynamic Pages ## Article: Creating Data Hooks for Dynamic Pages ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/creating-data-hooks-for-dynamic-pages.md ## Article Content: # Velo: Creating Data Hooks for Dynamic Pages Creating a data binding router hook allows you to intercept the process of a dynamic page's data getting bound to the page. This article takes you through what code you have to write in order to make a data binding router work. To learn more about what a data binding router hook is and why you would want to create one, see [About Data Binding Router Hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks-for-dynamic-pages.md). ### Add a Data Binding Router Hook To add a data binding router hook: 1. Go to your dynamic page located in your site's pages. 2. Click the Show More icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/10/25/30a7b8cf-6ad0-4562-8edb-40f5f54d27db/7c21e80f-9095-4668-90bf-14127f990bb9.png) and then click **Settings** to open the page's settings. 3. In the **Page Info** tab, scroll down to **Advanced Settings**, and click **Add Hooks** to display the Add Hooks panel. When adding a data binding router hook, function stubs for the data binding router hooks you chose are added to the **routers.js** file in your site's backend code. The data binding router hook functions are named with the following convention: _(params...) So if your dynamic page has the [prefix](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/making-dynamic-page-urls-meaningful-with-prefixes.md) **myPrefix**, the code added to the routers.js file for the **beforeRouter** hook should look like: myPrefix_beforeRouter(request) { // routing code ...} The hooks you can register are listed here in the order they run: * beforeRouter * customizeQuery * afterRouter To learn more about the hook functions, see the [Router API reference](http://wix.to/94BuAAs/wix-router.html). You can also register [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md) that run code before or after certain interactions with your site's collections. If you’ve done so, the data hooks will run between `customizeQuery()` and `afterRouter()`. ### beforeRouter( ) This hook is triggered before the router goes to the requested dynamic page. You can use this hook to route requests to a different page or return an error response. If you need the data that will be bound to the page to determine the correct response, use the `afterRouter()` hook instead. The `beforeRouter()` hook receives a [`WixRouterRequest`](http://wix.to/94BuAAs/wix-router.WixRouterRequest.html) object that contains information about the incoming request. The object has information about the URL used to reach the router, where the request came from, and who the request came from. Within the body of the hook you can write any code you want. The code you write there should help you determine how you want to respond to the request. The function needs to return a [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html) object that causes the router to continue its routing, or respond with an HTTP response code. If you want the router to continue to the same page it was originally going to with the same data, use the [`next()`](http://wix.to/94BuAAs/wix-router.html#next) function to return the appropriate [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html). However, if you want the router to do something other than it was originally going to do before reaching your hook, you can use the [`forbidden()`](http://wix.to/94BuAAs/wix-router.html#forbidden), [`notFound()`](http://wix.to/94BuAAs/wix-router.html#notFound), [`redirect()`](http://wix.to/94BuAAs/wix-router.html#redirect), or [`sendStatus()`](http://wix.to/94BuAAs/wix-router.html#sendStatus) functions to return a [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html). #### Example In this example we create a hook on the **dynamic** prefix that restricts certain requests from being routed to their intended target. The following code is placed in the **routers.js** file in your site's backend code. ```javascript import {forbidden, next} from 'wix-router'; export function dynamic_beforeRouter(request) { if (request.path.length > 1 && request.path[0] === "admin") { if (request.user && request.user.role == "siteOwner") return next(); else return forbidden(); } return next(); } ``` The functionality used to create data binding router hooks is contained in the [Router API](http://wix.to/94BuAAs/wix-router.html). To use some of this functionality, you need to import it.  Here we import the [`forbidden()`](http://wix.to/94BuAAs/wix-router.html#forbidden) and [`next()`](http://wix.to/94BuAAs/wix-router.html#next) functions. import {forbidden, next} from 'wix-router'; If you want to use more of the functionality from the Router API, you need to add it to the `import` statement. * * * In the hook function, we start by checking if the path begins with the string `"admin"`: if (request.path.length > 1 && request.path[0] === "admin") { // ... } If the path starts with anything other than "admin", the `if` is skipped and we tell the router to continue to where it was going before being intercepted by the hook: return next(); * * * However, if the path starts with `"admin"`, we check to see what type of user is making the request: if (request.user && request.user.role == "siteOwner") { // ... } If the user is the owner of the site, we once again tell the router to continue to where it was going before being intercepted by the hook: return next(); But if the user in not the owner of the site, we return a 403 response: return forbidden(); ### customizeQuery( ) This hook is triggered before the page's data query is executed. You can use this hook to further refine or change the query that will determine what data is bound to your page's dataset. The `customizeQuery()` hook receives a [`WixRouterRequest`](http://wix.to/94BuAAs/wix-router.WixRouterRequest.html) object that contains information about the incoming request, a string with the current route, and a [`WixDataQuery`](http://wix.to/94BuAAs/wix-data.WixDataQuery.html) object containing query which is going to be used to get the data for the page being routed to. Within the body of the hook you can write any code you want. The code you write there should help you determine if you want to continue with the initial data or if you want to change it in some way. The function needs to return a [`WixDataQuery`](http://wix.to/94BuAAs/wix-data.WixDataQuery.html) object that will be used to get the final data to be passed to the page that will be routed to. If you want the router to continue with the same data it was originally going to bind to the page, return the object that the function received in the `query` parameter. However, if you want the router use different data, you can either refine the current query using the functions of the [Data API](http://wix.to/94BuAAs/wix-data.html) or create a new query. #### Example In this example we create a hook on the **dynamic** prefix that filters the query for a certain route to only find active users. The following code is placed in the **routers.js** file in your site's backend code. ```javascript export function myRouter_customizeQuery(request, route, query) { if (route === "/users/{name}") return query.eq("status", "active"); else return query; } ``` The functionality used to create queries is contained in the [Data API](http://wix.to/94BuAAs/wix-data.html). To use some of this functionality, you need to import it.  * * * In the hook function, we start by checking if the route is going to a dynamic page that will be displaying users: if (route === "/users/{name}") // ... If it is going there, we refine the query to only get `"active"` users: return query.eq("status", "active"); Otherwise, we return the same query we received: return query; ### afterRouter( ) This hook is triggered after the router has bound the data, but before the page is displayed. You can use this hook to change the router's response based on the data that was retrieved.  The `afterRouter()` hook receives a [`WixRouterRequest`](http://wix.to/94BuAAs/wix-router.WixRouterRequest.html) object that contains information about the incoming request and a [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html) object containing information about the router's response. Within the body of the hook you can write any code you want. The code you write there should help you determine if you want to continue with the initial response or if you want to change it to another response. The function needs to return a [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html) object that causes the router to continue its routing, or respond with an HTTP response code. If you want the router to continue to the same page it was originally going to with the same data, return the object that the function received in the `response` parameter. However, if you want the router to do something other than it was originally going to do before reaching your hook, you can use the [`ok()`](http://wix.to/94BuAAs/wix-router.html#ok), [`forbidden()`](http://wix.to/94BuAAs/wix-router.html#forbidden), [`notFound()`](http://wix.to/94BuAAs/wix-router.html#notFound), [`redirect()`](http://wix.to/94BuAAs/wix-router.html#redirect), or [`sendStatus()`](http://wix.to/94BuAAs/wix-router.html#sendStatus) functions to return a [`WixRouterResponse`](http://wix.to/94BuAAs/wix-router.WixRouterResponse.html). To use the data from the router on a page that is different than the one the router was originally going to route to, you can use the \[`ok()`\] function to choose the page and pass it the `response.data` and `response.header` from the original response. For an example of this use case, see below. #### Example In this example we create a hook on the **dynamic** prefix that routes the user to one of two pages. We have two versions of a page, one for horizontal oriented images and another for vertical oriented ones. After the image is pulled from the database, we check which type of picture is to be shown and route to the page that corresponds to that image's orientation. The following code is placed in the **routers.js** file in your site's backend code. ```javascript import {ok} from 'wix-router'; export function myRouter_afterRouter(request, response) { if(response.status === 200 && response.page === "horizontal-pic") { if(response.data.picture.orientation === "vertical") return ok("vertical-pic", response.data, response.head); else return response; } return response; } ``` The functionality used to create data binding router hooks is contained in the [Router API](http://wix.to/94BuAAs/wix-router.html). To use some of this functionality, you need to import it.  Here we import the [`ok()`](http://wix.to/94BuAAs/wix-router.html#ok) function. import {ok} from 'wix-router'; If you want to use more of the functionality from the Router API, you need to add it to the `import` statement. * * * In the hook function, we start by checking if the response `status` is the okay status and if the page the response is routing to is the page that displays pictures with a horizontal orientation: if(response.status === 200 && response.page === "horizontal-pic") { // ... } If we find any other status or if the page we're responding with is not the one we have two versions of, we just return the original response: return response; * * * However, if the router is about to route to the page that displays horizontal images, we check the orientation of the image that will be displayed on the page. We do so by pulling the orientation information from the router's current response: if(response.data.picture.orientation === "vertical") // ... If the picture's orientation doesn't match the page it was being sent to, we route to a different page that does match its orientation and we take the `data` and `head` information from the original response and send it to the new page as well: return ok("vertical-pic", response.data, response.head); But if the picture's orientation already matches the page it was being sent to, we send it to where it was already going. return response; --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Aggregations in the Data API ## Article: Working with Aggregations in the Data API ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/aggregations/working-with-aggregations-in-the-data-api.md ## Article Content: # Velo: Working with Aggregations in the Data API  
**Note:** Before reading this article, you should be familiar with using the Data API. To learn more, see [Working with the Data API](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/working-with-the-data-api.md).
Using the aggregation functionality of the [Data API](http://wix.to/94BuAAs/wix-data.html) you can perform certain calculations on your collection data, as whole or on groups of items that you define, to retrieve meaningful summaries. You can also add filtering and sorting to your aggregations to retrieve exactly what you need. 
**Note:** Aggregations can only be used on collections you have created. They cannot be used on [Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
### Sample Data For demonstration purposes we use the following sample data. The data represents population statistics for cities collected over multiple years. In our examples, we assume the data is contained in a collection named **PopulationData**.  City, Population, State, Year Buffalo, 292000, NY, 2000 Buffalo, 261000, NY, 2010 Los Angeles, 3703000, CA, 2000 Los Angeles, 3796000, CA, 2010 Miami, 362000, FL, 2000 Miami, 401000, FL, 2010 New York, 8015000, NY, 2000 New York, 8192000, NY, 2010 Orlando, 195000, FL, 2000 Orlando, 240000, FL, 2010 San Diego 1228000, CA, 2000 San Diego 1306000, CA, 2010 San Francisco, 777000, CA, 2000 San Francisco, 805000, CA, 2010 If you want to follow along with the examples in this article, you can: 1. Create a collection on your site named **PopulationData**. 2. Save the above data in a .csv file. 3. [Import](https://support.wix.com/en/article/how-to-import-collection-data-in-the-content-manager) the .csv file into the **PopulationData** collection.  4. Copy and paste the code snippets found below into the code editor on one of your sites pages. ### Importing wix-data To work with aggregations, you will need to import `wix-data`. ```javascript import wixData from 'wix-data'; ``` ### Running Aggregations Running an aggregation is similar to running a query, but with some important differences.  To run an aggregation: 1. Create an aggregation using the `aggregate()` function. 2. Refine the aggregation using the functions described below. 3. Run the aggregation using the `run()` function. 4. Handle the aggregation results. For example, here is a simple aggregation that finds the largest population value in the **PopulationData** collection. ```javascript wixData.aggregate("PopulationData") .max("population") .run() .then( (results) => { let populationMax = results.items[0].populationMax; } ); ``` You can also use the following functions with an aggregation to modify the results you receive: * [`skip()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#skip) - Sets the number of items or groups to skip before returning aggregation results. * [`limit()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#limit) - Limits the number of items or groups the aggregation returns. ### Aggregation Results To handle aggregation results, use the following properties and functions on the object returned in the `aggregate()` function's Promise: * [`items`](http://wix.to/94BuAAs/wix-data.WixDataAggregateResult.html#items) - An array of the aggregated items or groups. Each value is contained in an object that is an element of the array. The structure of the objects depends on which aggregations have been run. * [`length`](http://wix.to/94BuAAs/wix-data.WixDataAggregateResult.html#length) - The number of items or groups in the aggregate results. * [`hasNext()`](http://wix.to/94BuAAs/wix-data.WixDataAggregateResult.html#hasNext) - Indicates if the aggregation has more results. Aggregation results are paged. So if your aggregation returns more results than the page size, you will have multiple pages of results. * [`next()`](http://wix.to/94BuAAs/wix-data.WixDataAggregateResult.html#next) - Retrieves the next page of aggregate results. >**Note:** Sometimes, the function that you run with the aggregation can cause the `_id`s to not be returned as strings, meaning the aggregated values would not be able to be used with repeaters. Learn more about repeaters and workarounds for this issue in [Working with Repeaters](https://dev.wix.com/docs/velo/articles/getting-started/working-with-repeaters.md). ### Aggregations Structure An aggregation is built with the following basic structure, where each part is optional.  The parts of an aggregation are explained below using the following example aggregation, which finds the cities whose population in 2010 was the largest in their respective states and the population was over 1,000,000.  ```javascript import wixData from 'wix-data'; // ... const filter = wixData.filter().eq("year", 2010); const having = wixData.filter().gt("maxPopulation", 1000000); wixData.aggregate("PopulationData") .filter(filter) .group("state") .max("population", "maxPopulation") .having(having) .descending("maxPopulation") .run() .then( (results) => { console.log(results.items); console.log(results.length); console.log(results.hasNext()); } ) .catch( (error) => { console.log(error.message); console.log(error.code); } ); ``` #### filter() Use the [`filter()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#filter) function to narrow down which items are included in an aggregation. For example, on line 9 of the aggregation above, the filter is used to filter out items where the `year` is not `2010`. The aggregate `filter()` function takes a [`WixDataFilter`](http://wix.to/94BuAAs/wix-data.WixDataFilter.html) object created using the [`wix-data.filter()`](http://wix.to/94BuAAs/wix-data.html#filter) function (line 5 above). Use any of the `WixDataFilter` filtering functions to build your `WixDataFilter` object. #### group() Use the [`group()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#group) function to group retrieved items together and then optionally calculate aggregated values and further filter the groups. Grouping is a powerful tool that allows you to aggregate data in groups instead of across a whole collection or part of a collection. When grouping is employed, aggregations are performed on the unique combinations that define each group. For example, without grouping you can find the city with the largest population in a collection or you can find the city with the largest population in a specific state. However, with grouping you can find the city with the largest population in each state. For example, on line 10 of the aggregation above, grouping is used to group together all cities in the same state. Then, on line 11, the [`max()`](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/max) function is used to get the largest population value from each of the state groups. You can perform the following aggregated calculations on groups: * [`min()`](https://www.wix.com/velo/reference/wix-data.WixDataAggregate.html#min) * [`max()`](https://www.wix.com/velo/reference/wix-data.WixDataAggregate.html#max) * [`avg()`](https://www.wix.com/velo/reference/wix-data.WixDataAggregate.html#avg) * [`sum()`](https://www.wix.com/velo/reference/wix-data.WixDataAggregate.html#sum) * [`count()`](https://www.wix.com/velo/reference/wix-data.WixDataAggregate.html#count) The `group()` function can also be used to create groups based on multiple fields. #### having() Use the [`having()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#having) function to narrow down which groups are included in an aggregation. The `having()` function differs from filter in that it is applied after the groupings are made. So `filter()` filters out **items** from the collection that you don't want considered at all while `having()` filters out **groups** that don't match the given criteria. For example, on line 12 of the aggregation above, the having is used to filter out groups where the `maxPopulation` is less or equal to `1000000`. The `having()` function takes a [`WixDataFilter`](http://wix.to/94BuAAs/wix-data.WixDataFilter.html) object created using the [`wix-data.filter()`](http://wix.to/94BuAAs/wix-data.html#filter) function (line 6 above). Use any of the `WixDataFilter` filtering functions to build your `WixDataFilter` object. #### Sorting Use the [`ascending()`](http://wix.to/94BuAAs/wix-data.WixDataAggregate.html#ascending) and [`decending()`](https://www.wix.com/velo/reference/wix-data/wixdataaggregate/descending) functions to sort the aggregation's resulting items or groups. For example, on line 13 of the aggregation above, the results are sorted in descending order based on the aggregated `maxPopulation` values. You can sort based on actual fields from your collection or virtual fields that are created as part of the grouping and aggregation process. For example, the sort on line 13 above is performed on the `maxPopulation` field, which is not a field in the collection, but a field created by the `max()` aggregation. ### Examples Here we present a number of common scenarios that demonstrate how to use aggregations in various ways. #### max() This example finds the largest population value of all the cities across all years. Notice that the key in the results is named `"populationMax"` because we are calling the `max()` function and passing it the `"population"` field ID. ```javascript wixData.aggregate("PopulationData") .max("population") .run() .then( (results) => { let items = results.items; } ); /* items is: * [{"_id": "0", "populationMax": 8192000}] */ ``` #### group(), max() This example finds the largest population value in each state across all years. ```javascript wixData.aggregate("PopulationData") .group("state") .max("population") .run() .then( (results) => { let items = results.items; } ); /* items is: * [ * {"_id": "FL", "populationMax": 401000}, * {"_id": "CA", "populationMax": 3796000}, * {"_id": "NY", "populationMax": 8192000} * ] */ ``` #### group(), count() This example finds the number of items for each state across all years. ```javascript wixData.aggregate("PopulationData") .group("state") .count() .run() .then( (results) => { let items = results.items; } ); /* items is: * [ * {"_id":"FL","count":4}, * {"_id":"CA","count":6}, * {"_id":"NY","count":4} * ] */ ``` #### group(...multiple-fields), max() You can also create groups based on multiple fields and then run aggregations on those groups. When grouping by multiple fields, each group is defined by a unique combination of all the fields in the group. For example, here is an aggregation that finds the largest population in each state for each year with population data. ```javascript wixData.aggregate("PopulationData") .group("state", "year") .max("population") .run() .then( (results) => { let items = results.items; } ); /* items is: * [ * { * "_id": {"state": "NY", "year": 2000}, * "populationMax": 8015000, * "state": "NY", * "year": 2000 * },{ * "_id": {"state": "FL", "year": 2000}, * "populationMax": 362000, * "state": "FL", * "year": 2000 * },{ * "_id": {"state": "CA", "year": 2000}, * "populationMax": 3703000, * "state": "CA", * "year": 2000 * },{ * "_id": {"state": "FL", "year": 2010}, * "populationMax": 401000, * "state": "FL", * "year": 2010 * },{ * "_id": {"state": "CA", "year": 2010}, * "populationMax": 3796000, * "state": "CA", * "year": 2010 * },{ * "_id":{"state": "NY", "year": 2010}, * "populationMax": 8192000, * "state": "NY", * "year": 2010 * } * ] */ ``` #### group(...multiple-fields), count() This example finds the number of items for each state per year. ```javascript wixData.aggregate("PopulationData") .group("state", "year") .count() .run() .then( (results) => { let items = results.items; } ); /* items is: * [ * { * "_id": {"state": "NY", "year": 2000}, * "count": 2, * "state": "NY", * "year": 2000 * },{ * "_id": {"state": "FL", "year": 2000}, * "count": 2, * "state": "FL", * "year": 2000 * },{ * "_id": {"state": "CA", "year": 2000}, * "count": 3, * "state": "CA", * "year": 2000 * },{ * "_id": {"state": "FL", "year": 2010}, * "count": 2, * "state": "FL", * "year": 2010 * },{ * "_id": {"state": "CA", "year": 2010}, * "count": 3, * "state": "CA", * "year": 2010 * },{ * "_id": {"state": "NY", "year": 2010}, * "count": 2, * "state": "NY", * "year": 2010 * } * ] */ ``` #### filter(), max() This example uses a filter to find the city with the largest population in the year 2000. ```javascript let filter = wixData.filter().eq("year", 2000); wixData.aggregate("PopulationData") .filter(filter) .max("population") .run() .then( (results) => { let items = results.items; } ); // items is: [{"_id": "0", "populationMax": 8015000}] ``` #### group(), max(), ascending() This example uses a sort to find the largest population in each state across all years and sorts them from least to greatest. ```javascript wixData.aggregate("PopulationData") .group("state") .max("population") .ascending("populationMax") .run() .then( (results) => { let items = results.items; } ); /* items is: * [ * {"_id": "FL", "populationMax": 401000}, * {"_id": "CA", "populationMax": 3796000}, * {"_id": "NY", "populationMax": 8192000} * ] */ ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Making Dynamic Page URLs Meaningful with Prefixes ## Article: Making Dynamic Page URLs Meaningful with Prefixes ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/making-dynamic-page-urls-meaningful-with-prefixes.md ## Article Content: # Velo: Making Dynamic Page URLs Meaningful with Prefixes >**Before reading this article you may want to read:** > * [About Dynamic Pages](https://support.wix.com/en/article/cms-about-dynamic-pages) > * [Creating A Unique Dynamic Page URL](https://support.wix.com/en/article/creating-a-unique-dynamic-page-url)   > * [Linking to Dynamic Pages](https://support.wix.com/en/article/cms-linking-to-dynamic-pages) The first section after the domain name of a [dynamic page URL](https://support.wix.com/en/article/creating-a-unique-dynamic-page-url) is the prefix. The prefix can help make your page URLs more meaningful for your visitors. For example, **recipes** is the prefix in the following URL:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2017/05/07/2fc37dda-f085-41cd-9a4b-4485c0c7afd7.png)
Every time you use a new prefix for a dynamic page URL, a new section is created in the sidebar for all pages with that prefix. This allows you to group pages in one section even if they are connected to different collections in your database.
**Tip:** When you create a dynamic page, the name of the collection that your page connects to is automatically added as the prefix to the URL. This prefix is only a suggested default and does not affect how your page connects to your collection. If you want to use a different prefix for your page, edit the URL and change it manually.
Let's look at an example to understand why this is useful. Let's say you have a site for a restaurant, and you keep your menu information in different collections: meals and beverages. Your meals collection has fields like dish name, allergy information, calories, which meal it's served for, and other meal-related information. Your beverages collection has fields like name, type of drink, domestic or imported, price per glass or bottle, and other drink-related information. Now you want to create dynamic pages to display your meals and beverages by category, but you want all those pages to be under the common URL prefix **menu**. To do this, you would create your dynamic pages with the URL `https://my-restaurant/menu` followed by other sections that make your URLs meaningful, and then fields that filter the dynamic content. In the end, you might have the following dynamic URLs: * `https://my-restaurant/menu/dishes/{meal}` * `https://my-restaurant/menu/drinks/{type}` In this case, **menu** is the prefix and **dishes** and **drinks** are additional sections you might manually add to your URLs to make them more meaningful. Your final page URLs would look like this: * `https://my-restaurant/menu/dishes/breakfast` * `https://my-restaurant/menu/dishes/lunch` * `https://my-restaurant/menu/dishes/dinner` * `https://my-restaurant/menu/drinks/beer` * `https://my-restaurant/menu/drinks/wine` * `https://my-restaurant/menu/drinks/scotch` * `https://my-restaurant/menu/drinks/soda` Now your page URLs communicate meaningful information for your visitors. The prefix also controls how your dynamic pages are grouped in the Pages menu. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Creating Previous and Next Buttons for a Dynamic Item Page with Code ## Article: Creating Previous and Next Buttons for a Dynamic Item Page with Code ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/creating-previous-and-next-buttons-for-a-dynamic-item-page-with-code.md ## Article Content: # Velo Tutorial: Creating Previous and Next Buttons for a Dynamic Item Page with Code When you want to add previous and next buttons to a dynamic page, you may be tempted to add two buttons, connect them to your page's dataset, and choose **Previous** and **Next** from the **Actions** list as you would on a regular page. Doing so will not work. To understand why it doesn't work, we need to understand a little bit about how dynamic item pages work. A dynamic item page decides which item to display using the [dynamic sections of the URL](https://support.wix.com/en/article/cms-creating-unique-dynamic-page-urls) that was used to reach the page. When the page loads, the proper item is then fetched from the database and is accessible to the page through the page's dynamic dataset. Since it is an item page, there will only be one item in the dataset. Therefore, there are no "previous" or "next" items to navigate to using the **Previous** and **Next** actions of the dataset. This article will demonstrate how you can use code to create previous and next buttons for a dynamic item page. We will do so by adding code to two pages. First, in an index page that your users use to reach your dynamic item pages, we will add code to store all the relevant item page links. Then, in the dynamic item page, we will add code to retrieve the links and use them in previous and next buttons that we will add to the page. In this article we assume that you are using an index page to present multiple items from one of your collections on a single page. Users select one of the items on your index page to reach the relevant dynamic item page. The order of the previous and next items on your item page will be determined by the index page that the user came from.
**Note:** You can also use the [getPreviousDynamicPage( )](https://www.wix.com/velo/reference/wix-dataset.DynamicDataset.html#getPreviousDynamicPage) and [getNextDynamicPage( )](https://www.wix.com/velo/reference/wix-dataset.DynamicDataset.html#getNextDynamicPage) functions or the **Previous dynamic page** and **Next dynamic page** [dataset actions](https://support.wix.com/en/article/dataset-actions-for-buttons-and-images) to create previous and next buttons on a dynamic item page. However, you cannot control the item order when using these functions.
### Index Page The index page can be a regular page or a dynamic list page. In either case, the page will have a dataset that is connected to your collection and a table or gallery that is set to link each item to a dynamic item page. To create the previous and next buttons on the dynamic item page we don't need to change the index page at all. We just need to add some code that will work behind the scenes. 
**Note:** The order of the previous and next items on your dynamic item page will be determined by the sort order of the dataset on the index page.
The code we will add to the index page does the following: * Retrieves the collection items from the dataset * Gets the relative URLs for the dynamic item page for each of the items * Stores the URLs in the user's local browser storage for us to use later Add the following code to your index page in the Page Code section. ```javascript import {local} from 'wix-storage-frontend'; const linkField = "link-to-item"; // replace this value $w.onReady(function () { $w("#myDataset").onReady(() => { const numberOfItems = $w("#myDataset").getTotalCount(); $w("#myDataset").getItems(0, numberOfItems) .then( (result) => { const dynamicPageURLs = result.items.map(item => item[linkField]); local.setItem('dynamicPageURLs', dynamicPageURLs); } ) .catch( (err) => { console.log(err.code, err.message); } ); } ); } ); ```
**Note:** See the appendix below to learn how to optimize the above code.
You'll need to do two things to make sure this code works for your specific situation. First, this code assumes your page's dataset has the ID **myDataset**. If it does not have that ID, you need to either change the dataset's ID to **myDataset** or change the code wherever it says `"#myDataset"` to reflect your dataset's actual ID. You can change an element's ID by selecting the element and editing the ID field in the Properties & Events panel. Second, this code needs to get the URLs to your dynamic item page from your collection. It can only do so if it knows which field in your collection the URLs are stored in. Since the field ID for that field will be different for every situation, you need to edit the code to reflect the proper field ID. On this line `const linkField = "link-to-item"; // replace this value` change the value inside double quotes to the correct field ID. Remember, each time you add a dynamic page, a field is automatically created in your collection with relative URLs for that page. In the Content Management System (CMS), you need to locate the field with the URLs for the dynamic item page that you want to add previous and next buttons to.  To find the **field ID** for a collection field, click on the vertical ellipses that appears when you hover over a field in the CMS and select **Edit**. You will see the **field ID** in the **Edit Field** window that pops up. Be sure to use the **field ID** and not the **Field Name**.
**Important:** This code gets the URLs for all the items in the dataset. If you have a very large collection, you may want to limit the number of items retrieved from the dataset.
**Note:** If you have more than one index page that leads to the same dynamic item page, you will need to add this code to each of those pages.
### Item Page On the dynamic item page, begin by adding two buttons to the page. Feel free to change the design and text of the buttons, but make sure not to add any links to the buttons. The code we will add to the dynamic item page does the following: * Disables the previous and next buttons * Retrieves the URLs stored in the user's local browser storage * Retrieves the URL of the current page * Gets the location of the current page's URL within the list of all the URLs * If there is a URL to a "previous" page, sets the previous button's link to that URL and enables the previous button * If there is a URL to a "next" page, sets the next button's link to that URL and enables the next button Add the following code to your dynamic item page in the Page Code section. ```javascript import {local} from 'wix-storage-frontend'; import wixLocationFrontend from 'wix-location-frontend'; $w.onReady(function () { $w("#previous").disable(); $w("#next").disable(); if (local.getItem('dynamicPageURLs')) { const dynamicPageURLs = local.getItem('dynamicPageURLs').split(','); const currentPage = '/' + wixLocationFrontend.prefix + '/' + wixLocationFrontend.path.join('/'); const currentPageIndex = dynamicPageURLs.indexOf(currentPage); if (currentPageIndex > 0) { $w("#previous").link = dynamicPageURLs[currentPageIndex - 1]; $w("#previous").enable(); } if (currentPageIndex < dynamicPageURLs.length - 1) { $w("#next").link = dynamicPageURLs[currentPageIndex + 1]; $w("#next").enable(); } } } ); ```
**Note:** See the appendix below to learn how to optimize the above code.
This code assumes that your buttons have the IDs **previous** and **next**. If your buttons do not have those IDs, you need to either change the IDs of the buttons to **previous** and **next** or change the code wherever it says `"#previous"` and `"#next"` to reflect actual IDs of your buttons. You can change an element's ID by selecting the element and editing the ID field in the Properties & Events panel.
Advanced: Storage Type In the code snippets above we use browser's storage to pass information between pages. There are two types of browser storage that you can access using the wix-storage-frontend API. In this article we use **local** storage. To use **session** storage instead, all you need to do is change the import statements in both code snippets accordingly. For more information, see [wix-storage-frontend](http://wix.to/94BuAAs/wix-storage.html) in the API Reference.
### API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/velo/reference/). **$w** * [$w.onReady( )](http://wix.to/94BuAAs/$w.html#onReady) - Sets the function that runs when all a page's elements have finished loading. **$w.Button** * [$w.Button.disable( )](http://wix.to/94BuAAs/$w.Button.html#disable) - Disables an element. * [$w.Button.enable( )](http://wix.to/94BuAAs/$w.Button.html#enable) - Enables an element. * [$w.Button.link](http://wix.to/94BuAAs/$w.Button.html#link) - Sets or gets the element's link. **wix-dataset** * [wix-dataset.getItems( )](https://www.wix.com/velo/reference/wix-dataset/dataset/getitems) - Returns the selected items from a dataset. * [wix-dataset.getTotalCount( )](https://www.wix.com/velo/reference/wix-dataset/dataset/gettotalcount) - Returns the number of items in a dataset that match its filter criteria. * [wix-dataset.onReady( )](https://www.wix.com/velo/reference/wix-dataset/dataset/onready) - Adds an event handler that runs when a dataset is ready. **wix-location-frontend** * [wix-location-frontend.prefix](http://wix.to/94BuAAs/wix-location.html#prefix) - Gets the prefix of the page's URL. * [wix-location-frontend.path](http://wix.to/94BuAAs/wix-location.html#path) - Gets the path of the page's URL. * [wix-location-frontend.to( )](http://wix.to/94BuAAs/wix-location.html#to) - Directs the browser to navigate to the specified URL. **wix-storage-frontend** * [wix-storage-frontend.getItem( )](http://wix.to/94BuAAs/wix-storage.Storage.html#getItem) - Gets an item from local or session storage. * [wix-storage-frontend.setItem( )](http://wix.to/94BuAAs/wix-storage.Storage.html#setItem) - Stores an item in local or session storage. **wix-window-frontend** * [wix-window-frontend.rendering.env](http://wix.to/94BuAAs/wix-window.rendering.html#env) - Gets the rendering environment. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrating External Databases with Your Wix Site ## Article: Integrating External Databases with Your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md ## Article Content: # About Integrating External Databases with Your Wix Site Wix's built-in data management solution is the [Wix Content Management System (CMS)](https://support.wix.com/en/article/cms-content-management-system-an-overview). While CMS collections cover a wide range of use cases for content-driven websites and applications, some projects may have specific requirements that can't be addressed by the integrated database solution. To address such cases, Wix allows users to connect an external database to Wix sites using an external database adaptor. Once the connection is set up, users can interact with these databases and use them to populate site elements as though they were Wix CMS collections. This article gives an overview of the ways you can connect an external database to Wix sites using an external database adaptor. ## Supported IDEs You can work CMS collections and connected external databases in the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/workspaces/wix-studio-working-with-the-code-panel.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/wix-ide/wix-studio-about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/git-integration-wix-cli-for-sites/about-git-integration-wix-cli-for-sites.md) (Wix Studio and Wix Editor). ## External database adaptors To establish a connection between a site and an external database, you need to prepare an external database adaptor. An external database adaptor is a server that translates Wix Data requests from your site to an external database’s protocol and translates the response back to a Wix Data format. From the Wix site’s point of view, the external database is a data collection and behaves exactly like an internal collection. This means that your external database collections can be managed via the [Wix Data APIs](https://dev.wix.com/docs/rest/business-solutions/cms/introduction.md), and used (with or without [datasets](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md)) to populate Wix UI elements like [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-a-table). **External database flows:**
![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f95cac67dd41ecd29bc34fdd19f91d35.png)
1. A Wix Data request is made to an external database collection. The request is sent to the adaptor. 2. The adaptor validates the request and translates it to a native database request in the required protocol. 3. The native protocol request is sent to the external database. 4. The external database sends a response in its native protocol and format. 5. The adaptor translates the response to a Wix Data format. 6. The translated response is sent to the Wix site and processed in the same way as an internal data response. ### Preparing an adaptor You can prepare an external database adaptor in one of the following ways: - [Deploy an out-of-the-box external database adaptor](#out-of-the-box-external-database-adaptors) for a single site. - [Build and deploy your own external database adaptor](https://dev.wix.com/docs/sdk/backend-modules/data/external-database-connections/sample-flows.md#backend-modules_data_external-database-connections_build-your-own-adaptor). ### Out-of-the-box external database adaptors Wix created out-of-the-box container image adaptors for several databases on external platforms, including AWS, Azure, and Google Cloud Platform. The following database types are supported with out-of-the-box adaptors:
Click to expand list of supported databases **MySQL** [MySQL](https://www.mysql.com/) supports vanilla MySQL versions 5.7 to 8.0. This includes variants like MariaDB and Percona Server for MySQL, as MySQL APIs are fully compatible. The following managed versions of MySQL are supported: - [Google Cloud SQL for MySQL](https://cloud.google.com/sql) - [Amazon RDS for MySQL and MariaDB](https://aws.amazon.com/rds/mysql/) - [Amazon Aurora](https://aws.amazon.com/rds/aurora/mysql-features/) - [Microsoft Azure MySQL](https://azure.microsoft.com/en-us/services/mysql/#overview) **Postgres** Open-source versions 9 to 13. The following managed versions are supported: - [Google Cloud SQL for Postgres](https://cloud.google.com/sql) - [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/) - [Amazon Aurora](https://aws.amazon.com/rds/aurora) - [Microsoft Azure Postgres](https://azure.microsoft.com/en-us/services/postgresql) **MS SQL** [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-2019) server can be connected both as an on-premises, self-managed installation or managed version where available: - [Google Cloud SQL](https://cloud.google.com/sql/docs/sqlserver/quickstart) - [Amazon RDS for SQL Server](https://aws.amazon.com/rds/sqlserver/) - [Microsoft Azure SQL database](https://azure.microsoft.com/en-us/products/azure-sql/database/#overview) **Google Cloud Spanner** - [Google Cloud Spanner](https://cloud.google.com/spanner): Fully managed relational database with unlimited scale, strong consistency, and up to 99.999% availability from Google. **Google Cloud BigQuery** - [Google Cloud BigQuery](https://cloud.google.com/bigquery): Fully managed, scalable, and serverless data warehouse by Google with built-in machine learning capabilities.

Learn how to deploy an out-of-the-box external database adaptor for: - [AWS](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/aws/integrate-your-aws-databases-with-your-wix-site.md) - [Google Cloud](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/google/integrate-your-google-cloud-platform-databases-with-your-wix-site.md) - [Microsoft Azure](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/microsoft-azure/integrate-your-microsoft-azure-database-with-your-wix-site.md) ## Connecting to an external database Once you've prepared an external database adaptor, you can use it to connect Wix sites to the external database. If you implemented your adaptor as part of a Wix app, the connection is made automatically when the app is installed on a site. Otherwise, you need to make the connection manually using either of the following methods: - [Add an external database collection in the Wix editor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/adding-and-deleting-an-external-database-collection.md). - Call [Create External Database Connection](https://dev.wix.com/docs/rest/business-solutions/cms/external-database-connection/create-external-database-connection.md) in the External Database Connections API. See the API's [sample flows](https://dev.wix.com/docs/rest/business-solutions/cms/external-database-connection/sample-flows.md) for detailed instructions. ### Use existing data You can use existing data from your external database in your Wix site. If you want your existing database table to be read-write on your site, it must contain the following columns: - **\_id** - **\_createdDate** - **\_updatedDate** - **\_owner** Tables without these columns are read-only in your Wix site. When creating new tables, include these columns to make the table writable from your site. ## See also - [CMS (Content Management System): An Overview](https://support.wix.com/en/article/cms-content-management-system-an-overview) - [About the Wix Data API](https://dev.wix.com/docs/rest/business-solutions/cms/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Adding and Deleting an External Database Collection ## Article: Adding and Deleting an External Database Collection ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/adding-and-deleting-an-external-database-collection.md ## Article Content: # Adding and Deleting External Database Collections > **Note:** This feature is only available for sites with certain premium plans. If you need to use this feature, you can [upgrade your site](https://www.wix.com/upgrade/website). Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Wix lets you [connect your site to an external database](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md) and then work with that database's collections in your site just as you would with our built-in collections. This article explains how to add and delete external database collections for your site. ## Before you begin Before adding an external database to your Wix site you need to set up or create an [external database adaptor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md#external-database-adaptors). > **Note:** You can also add external database collections to your site by calling [`createExternalDatabaseCollection()`](https://dev.wix.com/docs/sdk/backend-modules/data/external-database-connections/create-external-database-connection.md). For detailed instructions, see the [sample flows](https://dev.wix.com/docs/rest/business-solutions/cms/external-database-connection/sample-flows.md) of the External Database Connections API. ## Add external database collections 1. Select **Databases** from the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). 2. Click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/09/29/c0043bd2-1c97-4c1b-80b0-704e57d1e60f/e996cdd7-afcb-481f-be55-4414dd4895b1.png) icon next to **External Databases** and select **Add external database**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/90f9e838c8293d91715aa5aac9cfd555.png) 3. Choose the provider of the external database you wish to add and click **Next**.
4. In the Connect External Database screen: 1. Choose a namepace to group the external database's collections under. 2. Enter your endpoint URL for the external database adaptor. 3. Enter the database adaptor's secret key.
Your external database collections are now listed under the namespace you chose, and you can work with them as you would your other collections. ## Delete an external database collection Delete an external database collection from your site in the same way that you would delete a built-in data collection, using the Show More icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2022/09/04/45269098-373f-4d2c-8168-2fc5242c6024/c890c6a3-6762-4501-bdb4-e44c678108df.png). > **Note:** This only deletes the collection from your site. It does not delete it from the external database. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate Your Google Cloud Platform Databases with Your Wix Site ## Article: Integrate Your Google Cloud Platform Databases with Your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/google/integrate-your-google-cloud-platform-databases-with-your-wix-site.md ## Article Content: # Integrate Your Google Cloud Platform Databases with Your Wix Site > **Note:** This feature is only available for sites with certain premium plans. If you need to use this feature, you can [upgrade your site](https://www.wix.com/upgrade/website). Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Wix allows users to connect an external database to Wix sites using an [external database adaptor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md#external-database-adaprots). Once the connection is set up, users can interact with these databases and use them to populate site elements as though they were Wix CMS collections. With an external database adaptor, you can use your database hosted on GCP (Google Cloud Platform), and fully integrate it into your Wix site. This means your GCP data can be managed via the [Wix Data APIs](https://dev.wix.com/docs/rest/business-solutions/cms/introduction.md), and used (with or without [datasets](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md)) to populate Wix UI elements like [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-a-table). ## Overview This article walks you through the following steps: 1. Create secrets to securely store your database credentials. 2. Create and deploy a new Cloud Run service to host the Wix database adaptor. 3. Test Your Service 4. Connect your Wix site to your database. This tutorial assumes you already have your own database on GCP with a table containing some data. If you don't have this set up, refer to the [GCP documentation](https://cloud.google.com/docs) for instructions. This tutorial uses a container image with all the functionality needed to interface between your database and your Wix site. If you want to look under the hood, you can have a look at the [service plugin specifications](https://www.wix.com/velo/reference/spis/external-database-collections) for external database collections, and an overview of what's involved in [building your own adaptor](https://support.wix.com/en/article/velo-working-with-external-database-collections). ### Supported Databases Wix currently supports the following GCP databases: - [Cloud SQL for MySQL](https://cloud.google.com/sql/mysql) - [Cloud SQL for Postgres](https://cloud.google.com/sql/postgresql) - [Cloud SQL for Microsoft SQL Server](https://cloud.google.com/sql/sqlserver) - [Google BigQuery](https://cloud.google.com/bigquery) - [Google Cloud Spanner](https://cloud.google.com/spanner)
**Prerequisites for Read-Write Access to Your Database Tables:**
If you want your table to be read-write on your Wix site, it must contain the following columns: - `_id` - `_createdDate` - `_updatedDate` - `_owner` Tables without these columns will be read-only in your Wix site.
## Step 1 | Create secrets to securely store your database credentials The external database adaptor requires you to set some environment variables. Some of these variables, like the DB credentials, are sensitive and shouldn't be visible. Use the [GCP Secret Manager](https://console.cloud.google.com/security/secret-manager) to store and access these variables securely. > **Note:** We don't provide specific instructions for any configuration in GCP, as GCP's UI and flows may change. For further details on any of the instructions below, see the [Secret Manager documentation](https://cloud.google.com/secret-manager/docs/overview). ### Configure Secrets Store the following secret values for **all databases**: - **SECRET_KEY** A secret key that is used when connecting your Wix site to the adaptor. To create a level of authentication between your site and the adaptor, each request your site sends to the adaptor contains this value in the payload. - **PERMISSIONS (optional)** A JSON object that defines the read and write permissions for the tables in your database. If you don't set permissions, they default to admin. With admin permissions, only site admins can read or write to the external database from a Wix site. API calls or CMS connections to the database don't work for anyone who isn't a site admin. This means site code that communicates with the external database might not work for site visitors. When setting up permissions, make sure you use the ID of the table you want to read and write to, and not the database ID.
Learn more about configuring database permissions You can use the **PERMISSIONS** secret to customize the permission settings for each table in a database. The JSON object contains one key, **collectionPermissions**, whose value is an array of objects. Each object in this array contains the permissions settings for one of the collections in the database, using the following parameters: - **id:** The collection ID. - **read**: An array of strings representing which roles can read from the collection. Options: 'Admin', 'Member', 'Visitor' - **write**: An array of strings representing which roles can write to the collection. Options: 'Admin', 'Member', 'Visitor' Example **PERMISSIONS** value: ```json { "collectionPermissions": [ { "id": "Contacts", "read": ["Admin", "Member"], "write": ["Admin"] } ] } ``` > **Note:** You can save this object as a .json file and upload it to GCP as the value for the **PERMISSIONS** secret.
Customizing permissions for external databases is currently a developer preview feature, and may change. Changes to permissions settings aren't reflected in the editor.
#### Database-Specific Secrets Configuration In addition to the secrets above, each database requires its own specific secret configurations:
MySQL, Postgres, and MsSQL secret names and values - **USER:**  The username used to connect to your database instance. For Postgres instances, the default user name is **postgres**. For MySQL instances, the default user name is **root.** - **PASSWORD:**  The password used to connect to your SQL instance. - **DB:**  The database name to connect to. - **CLOUD_SQL_CONNECTION_NAME:**  The connection name for your DB instance.
BigQuery secret names and values - **DATABASE_ID:**  The BigQuery dataset ID. - **PROJECT_ID:**  Your Google Cloud Project ID.
Spanner secret names and values - **INSTANCE_ID:**  The Spanner instance ID. - **PROJECT_ID:**  Your Google Cloud Project ID.  
Make sure to create all of the required secrets for your database. All databases must include a **SECRET_KEY**.
### Create Secrets To create the secrets, go to the GCP Secret Manager and create a secret with the appropriate configuration. | Database | Secret Configurations | | -------------------------- | ------------------------------------------------------------------------- | | MySQL, Postgres, and MsSQL | SECRET_KEY
USER
PASSWORD
DB
CLOUD_SQL_CONNECTION_NAME | | BigQuery | SECRET_KEY
DATABASE_ID
PROJECT_ID | | Spanner | SECRET_KEY
INSTANCE_ID
DATABASE_ID
PROJECT_ID | To create a **PERMISSIONS** secret, instead of entering a **Secret value**, provide the .json file that contains your permission settings. ## Step 2 | Create and deploy a new Cloud Run service to host the Wix database adaptor Next, deploy the external database adaptor as a service on Cloud Run. To do this, you'll need to create the service using a prebuilt container, configured secrets, and environment variables. Then you'll need to configure the database connections. For further details on any of the instructions below, see the [Google Cloud Run documentation](https://cloud.google.com/run/docs). ### Create a Service Create a new service for your cloud run project with the following configuration: 1. Enter the following as the **Container Image URL**: ```bash gcr.io/wix-velo-api/velo-external-db ``` The **Service name** will default to **velo-external-db**. 2. Select the **All** for Autoscaling, and select **Allow unauthenticated invocations** for Authentication. ### Configure Access Roles, Secrets, and Environment Variables Configure the secrets that you defined earlier as environment variables for the service. To do this, perform the following steps: 1. Create a new service account that will run the Cloud Run service. 2. Grant access roles to the new account. 3. Create environment variables and assign the secrets' values. 4. Set the **Service account name**. We recommend you set the Service account name to match the Cloud Run instance name so that you can easily know which account is used by which instance. 5. Grant the following roles to the service account according to your database: All Databases: - **Secret Manager Secret Accessor** MySQL, Postgres, MsSQL: - **Cloud SQL Editor** BigQuery: - **BigQueryAdmin** Spanner: - **Cloud Spanner Database User** 6. Define 2 environment variables according to your database. The names and values for the variables are as follows: | NAME | VALUE | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | TYPE | MySQL database: **mysql
**Postgres database: **postgres**
MsSQL server database: **mssql**
BigQuery database: **bigquery**
Spanner database: **spanner** | | CLOUD_VENDOR | Google Cloud Platform: **gcp** | 7. Make the secrets you configured and created in step 1 [accessible to Cloud Run](https://cloud.google.com/run/docs/configuring/services/secrets) by adding the secrets information and exposing them as a environmental variables. Refer to [the table above](#create-secrets) to see which secrets your database needs. For each secret, assign the environment variable the same name as the secret and ensure you've selected the latest version.
If you get a warning that the service account doesn't have permissions to access the selected secret, you are no longer configuring the service account that you set up in the previous steps.
### Configure the Database Connection [Deploy the container](https://cloud.google.com/run/docs/quickstarts/deploy-container). Get the cloud run service's URL. This URL is used to connect to your database adaptor service and to configure the external collection on your Wix site. **Note:** If you change the value of a secret, you must redeploy the service for the new value to take effect. ## Step 3 | Test Your Service You can test that your service is working by making a quick request using Curl. Use the Curl command below, replacing the URL with your URL from Step 2 above, and replacing the  **secretKey** value with your secret. ```bash curl -L -X POST 'https://velo-postgres-db-cacacapca-uc.a.run.app/schemas/list' \ -H 'Content-Type: application/json' \ --data-raw '{ "requestContext": { "settings": { "secretKey": "ourLittleSecret" }, "role": "OWNER" } }' ``` The output provides a list of tables and their columns from your database. If you have python installed, you can pipe the output to  **python -m json.tool** and it will give you nicely formatted JSON. The formatted output contains the list of tables and their columns from your database. ```json { "schemas": [ { "id": "contacts", "displayName": "contacts", "allowedOperations": [ "get", "find", "count", "update", "insert", "remove" ], "maxPageSize": 50, "ttl": 3600, "fields": { "name": { "displayName": "name", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "_createddate": { "displayName": "_createddate", "type": "datetime", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "email": { "displayName": "email", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] } } } ] } ``` ## Step 4 | Connect your Wix site to your database Now that you have a database and an adaptor service, you're ready to add the database as an external collection on your site. > **Note:** You can only add external collections to your site if you have a [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). 1. Go to the **Databases** section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). 2. Click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/09/29/c0043bd2-1c97-4c1b-80b0-704e57d1e60f/e996cdd7-afcb-481f-be55-4414dd4895b1.png) icon next to **External Databases** and select **Add external database**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7529ef4e207ccfa2e9b533d6a4bf14c6.png) 3. Choose **Google Cloud** as the cloud provider of the external collection being added, then click **Next**. ![Choosing Google on Connect External Database window](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ec776e88f9f4a9bc8e35f11a0df3edb1.png) 4. Enter a **name** for your external collection's namespace. 5. Copy and paste your adaptor service's URL into the **endpoint URL** field. 6. Enter your database adaptor's **secret key**. 7. Click **Connect**. ![Configuring the external database connection](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bc1c51b43b4810317173c04b12074214.png) The collection displays the tables. If your table contains the **\_id**, **\_createdDate**, **\_updatedDate**, and **\_owner** fields, you can add data to the table directly from the CMS.
**Important:** The CMS doesn't currently support deleting from external collections. You can delete records from your collection using Wix Data's [`remove()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/remove.md) method.
The external connection and its collections are displayed under **External Databases**. ![External database connections in Wix Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/42e8f9cc4d9a7c51dcd3032189e2a5c9.png) You can now use the [Wix Data API](https://dev.wix.com/docs/sdk/backend-modules/data/introduction.md) with this collection as well as connect [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/displaying-collection-content-in-a-table) to your MySQL, Postgres, MsSQL, BigQuery, and Spanner databases. Try the following [Wix Javascript SDK](https://dev.wix.com/docs/sdk/articles/get-started/about-the-wix-java-script-sdk.md) code to query your external data using your collection name and table name: ```javascript import { items } from '@wix/data'; export async function externalQuery() { try { const results = await items.query("/").find(); return results; } catch (err) { return err; } } ``` > **Notes:** > - You must first install the `@wix/data` package on your site. > - You can only call this code from your site's backend. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate Your AWS Databases with Your Wix Site ## Article: Integrate Your AWS Databases with Your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/aws/integrate-your-aws-databases-with-your-wix-site.md ## Article Content: # Integrate Your AWS Databases with Your Wix Site
__Important:__ This guide relies on AWS App Runner, which is closed to new customers as of April 30, 2026. New AWS customers who sign up after that date won’t be able to follow this guide as written. Existing App Runner customers are not affected and can continue to use the service as normal. For more details, see [AWS App Runner availability change](https://docs.aws.amazon.com/apprunner/latest/dg/apprunner-availability-change.html).
> **Note:** This feature is only available for sites with certain premium plans. If you need to use this feature, you can [upgrade your site](https://www.wix.com/upgrade/website). Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Wix allows users to connect an external database to Wix sites using an [external database adaptor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md#external-database-adaprots). Once the connection is set up, users can interact with these databases and use them to populate site elements as though they were Wix CMS collections. With an external database adaptor, you can use your database hosted on GCP (Google Cloud Platform), and fully integrate it into your Wix site. This means your GCP data can be managed via the [Wix Data APIs](https://dev.wix.com/docs/rest/business-solutions/cms/introduction.md), and used (with or without [datasets](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md)) to populate Wix UI elements like [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-a-table). ## Overview This article walks you through the following: 1. Creating secrets to securely store your database credentials and your Wix Secret Key. 2. Creating and deploying a new App Runner service to host the Wix database adaptor. 3. Testing your service 4. Connecting your Wix site to your database. This tutorial assumes you already have your own database on AWS with a table containing some data. If you don't have this set up, refer to the [AWS documentation](https://docs.aws.amazon.com/) for instructions. This tutorial uses a container image with all the functionality needed to interface between your database and your Wix site. If you want to look under the hood, you can have a look at the [service plugin specifications](https://www.wix.com/velo/reference/spis/external-database-collections) for external database collections, and an overview of what's involved in [building your own adaptor](https://support.wix.com/en/article/velo-working-with-external-database-collections). ### Supported Databases We currently support the following AWS databases: - [Amazon RDS for MySQL](https://aws.amazon.com/rds/mysql/) - [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/) - [Amazon Aurora](https://aws.amazon.com/rds/aurora/features/#:~:text=Close-,Amazon%20RDS,-RDS%20for%20MySQL)
**Prerequisites for Read-Write Access to Your Database Tables:**
If you want your table to be read-write on your Wix site, it must contain the following columns: - `_id` - `_createdDate` - `_updatedDate` - `_owner` Tables without these columns will be read-only in your Wix site.
## Step 1 | Create secrets to securely store your database credentials and your Wix Secret Key The external database adaptor requires you to set some environment variables. Some of these variables, like the DB credentials, are sensitive and should not be visible. Use the [AWS Secrets Manager](https://us-east-1.console.aws.amazon.com/secretsmanager/listsecrets) to store and access these variables securely. > **Note:** We don't provide specific instructions for any configuration in AWS, as AWS's UI and flows may change. For further details on any of the instructions below, see the [AWS Secrets Manager documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html). #### Create a New Secret for RDS Databases For RDS databases, you need to store 5 secret values: - **USERNAME** The username used to connect to your database instance. - **PASSWORD** The password used to connect to your database instance. - **DB** The name of the database to connect to. - **SECRET_KEY** A secret key used to connect your Wix site to the adaptor. To create a level of authentication between your site and the adaptor, each request your site sends to the adaptor contains this value in the payload. - **PERMISSIONS (optional)** A stringified JSON object that defines the read and write permissions for the tables in your database. It's important to note the following about setting permissions: - If you don't set permissions, they default to admin. With admin permissions, only site admins can read or write to the external database from a Wix site. API calls or CMS connections to the database don't work for anyone who isn't a site admin. This means site code that communicates with the external database might not work for site visitors. - When setting up permissions, make sure you use the ID of the table you want to read and write to, and not the database ID. **Note:** Customizing permissions for external databases is currently a developer preview feature, and may change. Changes to permissions settings are not reflected in the Wix Editor.
Learn more about configuring database permissions You can use the **PERMISSIONS** secret to customize the permission settings for each table in a database. The JSON object contains one key, **collectionPermissions**, whose value is an array of objects. Each object in this array contains the permissions settings for one of the collections in the database, using the following parameters: - **id:** The collection ID. - **read**: An array of strings representing which roles can read from the collection. Options: 'Admin', 'Member', 'Visitor' - **write**: An array of strings representing which roles can write to the collection. Options: 'Admin', 'Member', 'Visitor' Example **PERMISSIONS** value: ```json { "collectionPermissions": [ { "id": "Contacts", "read": ["Admin", "Member"], "write": ["Admin"] } ] } ``` Note that you need to store the **PERMISSIONS** JSON object as a string. To do this, first write the object as usual and use a tool such as your browser's developer tools console to stringify it.
> **Note** > When you store a new secret and select **Credentials for Amazon RDS database,** the Secrets Manager automatically stores the **USERNAME**, **PASSWORD**, and other connection information for you. You will need to manually add the **SECRET_KEY**, **DB**, and **PERMISSIONS** (optional) values as shown in the steps below. ### Create a new role for an RDS instance Create a new role for an RDS Instance in AWS's IAM manager console with permission to access the above secrets. See the [IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) for more details. You'll assign this role to the App Runner service you create in the next step. ## Step 2 | Create and deploy a new App Runner service to host the Wix database adaptor Now you'll create the service that will run the adaptor and make it available to your Wix site. For further details on any of the instructions below, see the [AWS Management Console documentation](https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/what-is.html). 1. Create a new AWS App Runner from the AWS Management Console. 2. Enter the following as the **Container Image URL**: ```bash public.ecr.aws/p2z5s3h8/wix-velo/velo-external-db:latest ``` 3. Add the following additional key/value pairs: | Source | Environment variable name | Environment variable value | | ---------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Plain text | **CLOUD_VENDOR** | All databases: **aws** | | Plain text | **TYPE** | MySQL database: **mysql**
PostgreSQL database: **postgres**
Aurora_mysql:**mysql**
Aurora_postgresql:**postgres** | | Plain text | **SECRET_NAME** | All databases:
The secret you created for your database. Ours is **tutorial_secret** | 4. Assign the runner the role you created in step 1. 5. Create and deploy the service. 6. Record the URL of you App Runner. ## Step 3 | Test Your Service You can test that your service is working by making a quick request using Curl. Use the Curl command below, replacing the URL with the URL of your App Runner, and replacing the **secretKey** value with your secret. ```bash curl -L -X POST 'https://abcdbajfc.us-east-1.awsapprunner.com/schemas/list' \ -H 'Content-Type: application/json' \ --data-raw '{ "requestContext": { "settings": { "secretKey": "myBigSecret" }, "role": "OWNER" } }' ``` The output provides a list of tables and their columns from your database. If you have python installed, you can pipe the output to **python -m json.tool** and it will give you nicely formatted JSON. The formatted output containing a list of tables and their columns: ```json { "schemas": [ { "id": "contacts", "displayName": "contacts", "allowedOperations": [ "get", "find", "count", "update", "insert", "remove" ], "maxPageSize": 50, "ttl": 3600, "fields": { "name": { "displayName": "name", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "_createddate": { "displayName": "_createddate", "type": "datetime", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "email": { "displayName": "email", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] } } } ] } ``` ## Step 4 | Connect your Wix site to your database Now that you have a database and an adaptor service, you're ready to add the database as an external collection on your site. > **Note:** You can only add external collections to your site if you have a [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). 1. Go to the **Databases** section of the Code sidebar (Wix Editor) or the Code sidebar (Wix Studio). 2. Click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/09/29/c0043bd2-1c97-4c1b-80b0-704e57d1e60f/e996cdd7-afcb-481f-be55-4414dd4895b1.png) icon next to **External Databases** and select **Add external database**. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7529ef4e207ccfa2e9b533d6a4bf14c6.png) 3. Choose **Amazon Web Services** as the provider of the external collection being added, then click **Next**. ![Choosing AWS on Connect External Database window](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84c4e9fb57b8f168be8346ed3452bef1.png) 4. Enter a **name** for your external collection's namespace. 5. Copy and paste your adaptor service's URL into the **endpoint URL** field. 6. Enter your database adaptor's **secret key**. 7. Click **Connect**. ![Configuring the external database connection](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d2f2fef57820aad78b7d2bfc25426af7.png) The Content Management System (CMS) displays the tables. If your table contains the **\_id**, **\_createdDate**, **\_updatedDate**, and **\_owner** fields, you can add data to the table directly from the CMS.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate Your Microsoft Azure Database with Your Wix Site ## Article: Integrate your Microsoft Azure Database with your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/microsoft-azure/integrate-your-microsoft-azure-database-with-your-wix-site.md ## Article Content: # Integrate your Microsoft Azure Database with your Wix Site > **Note:** This feature is only available for sites with certain premium plans. If you need to use this feature, you can [upgrade your site](https://www.wix.com/upgrade/website). Learn more about [premium plans](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). Wix allows users to connect an external database to Wix sites using an [external database adaptor](https://dev.wix.com/docs/develop-websites/articles/databases/external-databases/overview/integrating-external-databases-with-your-wix-site.md#external-database-adaprots). Once the connection is set up, users can interact with these databases and use them to populate site elements as though they were Wix CMS collections. With an external database adaptor, you can use your MySQL database hosted on Azure and fully integrate it into your Wix site. This means your Azure data can be managed via the [Wix Data APIs](https://dev.wix.com/docs/rest/business-solutions/cms/introduction.md), and used (with or without [datasets](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md)) to populate Wix UI elements like [repeaters](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) and [tables](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-a-table). ## Overview This article walks you through the following: 1. Creating a web app. 2. Setting up a key vault and secrets. 3. Testing your adaptor. 4. Connecting your Wix site. This tutorial assumes you already have your own Azure database server for MySQL with a table containing some data. If you don't have this set up, refer to the [Azure Database for MySQL documentation](https://learn.microsoft.com/en-us/azure/mysql/) for instructions. This tutorial uses a container image with all the functionality needed to interface between your database and your Wix site. If you want to look under the hood, you can have a look at the [service plugin specifications](https://www.wix.com/velo/reference/spis/external-database-collections) for external database collections, and an overview of what's involved in [building your own adaptor](https://support.wix.com/en/article/velo-working-with-external-database-collections).
**Prerequisites for Read-Write Access to Your Database Tables:**
If you want your table to be read-write on your Wix site, it must contain the following columns: - `_id` - `_createdDate` - `_updatedDate` - `_owner` Tables without these columns will be read-only in your Wix site.
> **Note:** We don't provide specific instructions for any configuration in Azure, as Azure's UI and flows may change. For further details on any of the instructions below, see the [Azure Database for MySQL documentation](https://learn.microsoft.com/en-us/azure/mysql/). ## Step 1 | Create a Web App A web app service is needed to run the adaptor and make it accessible to your Wix site. To deploy the adaptor as a web app: 1. Create a web app service to run the container. 2. Use the following **Container Image URL**: ```bash veloex/velo-external-db:latest ``` 3. Add the virtual network to the web app. ## Step 2 | Set up a Key Vault and secrets The external database adaptor uses environment variables to configure database credentials and connection details. Some of these variables are sensitive and shouldn't be visible. Use the Key Vault to store and access these variables securely. 1. Create a new Key Vault. 2. Define and then create the following 5 secrets: * **HOST**: The host name of your database instance. It can be found on your instance's overview page as the **Server name**. * **USER**: The username used to connect to your database instance. It can be found on your instance's overview page as the **Server admin login name** as shown below. * **PASSWORD**: The server admin login password used to connect to your SQL instance. * **DB**: The database name. When you created your database in the previous steps, you called the database **mysqlTutorial.** You can find this in the MySQL database server overview - under **Available Resources** at the bottom of the page. * **SECRETKEY**: A secret key that's used when connecting your Wix site to the adaptor. To create a level of authentication between your site and the adaptor, each request your site sends to the adaptor contains this value in the payload. For the tutorial, use **myBigSecret** as the SECRETKEY value. * **PERMISSIONS** (optional) A stringified JSON object that defines the read and write permissions for the tables in your database. When setting up permissions, note a couple of important things: - If you don't set permissions, they default to admin. With admin permissions, only site admins can read or write to the external database from a Wix site. API calls or CMS connections to the database don't work for anyone who isn't a site admin. This means site code that communicates with the external database might not work for site visitors. - When setting up permissions, make sure you use the ID of the table you want to read and write to, and not the database ID. > **Note:** > Customizing permissions for external databases is currently a developer preview feature, and may change. > Changes to permission settings are not reflected in the editor.
Learn more about configuring database permissions You can use the **PERMISSIONS** secret to customize the permission settings for each table in a database. The JSON object contains one key, **collectionPermissions**, whose value is an array of objects. Each object in this array contains the permissions settings for one of the collections in the database, using the following parameters: * **id:** The collection ID. * **read**: An array of strings representing which roles can read from the collection. Options: 'Admin', 'Member', 'Visitor' * **write**: An array of strings representing which roles can write to the collection. Options: 'Admin', 'Member', 'Visitor' Example **PERMISSIONS** value: ```json { "collectionPermissions": [ { "id": "Contacts", "read": ["Admin", "Member"], "write": ["Admin"] } ] } ``` Note that you need to store the **PERMISSIONS** JSON object as a string. To do this, first write the object as usual and use a tool such as your browser’s developer tools console to stringify it.
3. Create an identity for the web app. 4. Add the web app's Identity to the Key Vault's access policies. 5. Configure Web App Environment Variables For each of the secrets you created in the previous section, add an application setting as per the table below: Substitute the value for "VaultName" with your own key vault name. | Name | Value | | ----------- | -------------------------------------------------------------------------- | | USER | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=USER) | | HOST | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=HOST) | | DB | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=DB) | | PASSWORD | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=PASSWORD) | | SECRET_KEY | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=SECRETKEY) | | PERMISSIONS | @Microsoft.KeyVault(VaultName=my-velo-DB-key-vault;SecretName=PERMISSIONS) | > **Note:** > When configuring the **SECRETKEY,** the application setting is **SECRET\_KEY** but the secret name is **SECRETKEY.** This is because you cant use an "\_" in a secret name in Azure. 6. Add 2 more application settings as per the table below: | Name | Value | | ------------ | ----- | | TYPE | mysql | | CLOUD_VENDOR | azure | ## Step 3 | Test your adaptor You can test that your service is working by making a quick request using Curl. Use the curl command below, replacing the URL with your URL from the web app overview page, and replacing the **secretKey** value with your secret key. ```bash curl -L -X POST 'https://velo-external-db.azurewebsites.net/schemas/list' \ -H 'Content-Type: application/json' \ --data-raw '{ "requestContext": { "settings": { "secretKey": "myBigSecret" }, "role": "OWNER" } }' ``` The output provides a list of tables and their columns from your database. If you have python installed, you can pipe the output to **python -m json.tool** and it will give you nicely formatted JSON. The formatted output containing a list of tables and their columns: ```json { "schemas": [ { "id": "contacts", "displayName": "contacts", "allowedOperations": [ "get", "find", "count", "update", "insert", "remove" ], "maxPageSize": 50, "ttl": 3600, "fields": { "name": { "displayName": "name", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "_createddate": { "displayName": "_createddate", "type": "datetime", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, "email": { "displayName": "email", "type": "text", "queryOperators": [ "eq", "lt", "gt", "hasSome", "and", "lte", "gte", "or", "not", "ne", "startsWith", "endsWith" ] }, ... ``` ## Step 4 | Connect to your Wix site Now that you have a database and an adaptor service, you're ready to add the database as an external collection on your site. > **Note:** You can only add external collections to your site if you have a [premium plan](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/premium-plans/about-premium-plans.md). 1. In the editor, select **Databases** from the Code sidebar (Wix Editor) or Code sidebar (Wix Studio). 2. Click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2021/09/29/c0043bd2-1c97-4c1b-80b0-704e57d1e60f/e996cdd7-afcb-481f-be55-4414dd4895b1.png) icon next to **External Databases** and select **Add external database**. ![Add external database](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7529ef4e207ccfa2e9b533d6a4bf14c6.png) 3. Choose **Azure** as the provider of the external collection being added, then click **Next**. ![Choosing Azure on Connect External Database window](https://wixmp-833713b177cebf373f611808.wixmp.com/images/261877204acb39f6b2f0f15439e6c488.png) 4. Enter a **name** for your external collection's namespace. 5. Copy and paste your adaptor service's URL into the **endpoint URL** field. 6. Enter your database adaptor's **secret key**. 7. Click **Connect**. ![Configuring the external database connection](https://wixmp-833713b177cebf373f611808.wixmp.com/images/861290b834b1a0f5297ede8335a5e4bd.png) The Content Management System (CMS) displays the tables. If your table contains **_id**, **_createdDate**, **_updatedDate**, and **_owner** fields, you can add data to the table directly from the CMS.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Apps Made by Wix ## Article: About Apps Made by Wix ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/about-apps-made-by-wix.md ## Article Content: # About Apps Created by Wix Wix offers a range of apps and [business solutions](https://support.wix.com/en/business-solutions-apps), enabling users to sell products and services, run events, write blogs, manage restaurants, and more. Wix business solutions are comprehensive apps created by Wix for managing a specific type of business. Users add Wix business solutions to their sites by installing [apps created by Wix](https://www.wix.com/app-market/collection/made-by-wix). To make an informed choice about how to work with these apps, follow the path below. ## Get acclimated First, we recommend you review the [Integrating with Wix's Business Solutions](https://dev.wix.com/docs/develop-websites/articles/getting-started/integrate-with-wix-s-business-solutions.md) article. In addition to Wix's business solutions, Wix also offers other solutions you can integrate with, such as media support, form submission, location management, loyalty programs, and more. ## Get started A great way to learn about Wix's business solutions is to try out tutorials related to the business solutions relevant to you. ## Get integrated You can integrate with Wix’s business solutions and resources in the following ways: * **APIs** for connecting your services to Wix sites by gaining access to a business solution’s [data](https://dev.wix.com/docs/sdk/backend-modules/data/introduction.md). We use the term *API* here broadly to include [Velo APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/velo-for-blocks.md), [SDKs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/java-script-sdk.md) and [GraphQL](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/graph-ql.md). * **Service plugins** for injecting your own functionality directly into a Wix business solution. Learn more about [service plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md). * **Replacement of business solution pages** for customizing the user experience by replacing default Wix business solution pages with your own. Learn more about [replacing a Wix Business solution's app page](https://dev.wix.com/docs/develop-websites/articles/wix-apps/replace-a-wix-business-app-page-with-your-own-custom-version.md). ## See also - [Integrating with Wix's Business Solutions](https://dev.wix.com/docs/develop-websites/articles/getting-started/integrate-with-wix-s-business-solutions.md) - [Service Plugins](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/service-plugins-formerly-spis/custom-app-extensions-using-spis.md) - [Replacing a Wix Business Solution's App Page with your Own Custom Version](https://dev.wix.com/docs/develop-websites/articles/wix-apps/replace-a-wix-business-app-page-with-your-own-custom-version.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Replace a Wix Business App Page with Your Own Custom Version ## Article: Build a custom Wix Business app page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/replace-a-wix-business-app-page-with-your-own-custom-version.md ## Article Content: # Replace a Wix Business App Page with Your Own Custom Version You can replace some of the Wix's default app pages, such as the Booking Calendar or the Plans & Pricing page, with your own customized versions. Modify or extend the page’s design and functionality however you like to suit your precise business needs. By adding Velo code to the page and using Wix APIs, you can easily integrate with the app’s page flow and access contextual app data, providing users with a seamless app experience. ## 📖  Step-by-step guides For detailed instructions on how to implement each type of custom page, see: * [Build a custom Booking Service page](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/build-a-custom-booking-service-page.md) * [Build a custom Booking Calendar](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/build-a-custom-booking-calendar-page.md) * [Build a custom Pricing Plans page](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-pricing-plans/build-a-custom-pricing-plans-page.md) ## ⚙️  How custom app pages work To implement a custom page's functionality and integrate it into the app's page flow, you need to add code to the page and use Velo APIs. Your code has to do several things: 1. Fetch data from the Wix app about the content to display. 2. Handle UI initialization and interactions, and implement any customized business logic. 3. Direct users to the next page in the app's flow. ![replace-diagram](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bc177ef473d2d6ab29e7c031ef0090c4.gif) ## 📽️  Demo video Watch a quick demo to see how easy it is to build you own custom page. ## 🎮  Example site Check out our [example site](https://www.wix.com/velo-examples/bookings-reflow) and [template](https://manage.wix.com/edit-template/from?originTemplateId=905d81a9-4473-44cb-bf1e-058ef27dd476&templateId=4548e92c-e357-43d3-b348-37c86000b80d&autoDevMode=true) to explore the code and APIs and learn how to build a custom Booking Calendar page. [![reflow-example-image](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2c661a879b35f0abe9e7d8247da4afdf.png)](https://www.wix.com/velo-examples/bookings-reflow) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "Products" Collection Fields ## Article: Wix Stores "Products" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-products-collection-fields.md ## Article Content: # Velo: Wix Stores "Products" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Products collection in code, refer to it as `"Stores/Products"`. ```javascript // Query all products wixData .query("Stores/Products") .find() .then((results) => { // handle the results }); // Query all products, including variants wixData .query("Stores/Products") .find({ appOptions: { // Include product variants in the query. Defaults to `false`. includeVariants: true, }, }) .then((results) => { // handle the results }); ``` This document describes the permissions and fields in your Products collection.
**Important:** - You can query up to 100 items from the Products collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error. - You can use up to 5 filters in one query. This limit can be extended by using the 'and' operator to combine queries.
By default, querying hidden products requires the appropriate permissions. However, you can allow your visitors to [temporarily bypass this requirement](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md#temporarily-bypassing-collection-permissions) by setting the `suppressAuth` property to `true`. ```javascript // Query all products, including variants and hidden products wixData.query("Stores/Products") .find({ appOptions: { // Include product variants in the query. Defaults to `false`. includeVariants: true, // Explicitly allow querying hidden products. Defaults to `false`. suppressAuth: true // Include hidden products in the query. Defaults to `false`. includeHiddenProducts: true } }) .then((results) => { // handle the results }); ``` ### Permissions The Products collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): - **Read:** Anyone - **Create:** None - **Update:** None - **Delete:** None You cannot change the Products collection's permissions. ### Fields > **Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The product ID (GUID) that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Name (name) **Description**: The name of the product. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Date and time the product was last changed. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Description (description) **Description**: The product description in rich text. **Type**: Rich text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Main Media (mainMedia) **Description**: Displays the main media item (image or video) for this product as it appears in the Store Manager. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Media Items (mediaItems) **Description**: Lists all the media items available for this product in an array of JSON objects. **Type**: Media Gallery **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```javascript [ { type: "Image", src: "wix:image://v1/68d3a9_1de7529c444b4c9eb38401f8efe0cad2.jpg/flowers.jpg/#originWidth=1970&originHeight=1120", title: "title", description: "whatever first line\nthe second line.", link: "https://www.whatever.com", }, { type: "Video", src: "wix:video://v1/11062b_03c50f8fe4a34cc297a984e483a282ef/simpsons.mov#posterUri=96fbec_52cd5b00f7fd48e0a83bc1fb1dfb06ea.jpg &posterWidth=1920&posterHeight=1080", title: "title", description: "whatever first line\nthe second line.", link: "https://www.whatever.com", thumbnail: "wix:image://v1/68d3a9_1de7529c444b4c9eb38401f8efe0cad2.jpg/flowers.jpg/#originWidth=1970&originHeight=1120", }, ]; ``` #### SKU (sku) **Description**: The product's stock-keeping unit number. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes  **Can be filtered**: Yes **Read-only**: Yes #### Ribbon (ribbon) **Description**: The product's ribbon. Used to highlight relevant information about a product. For example, `"New Arrival"` or `"5 items left"`. **Note**: This field replaces the deprecated \`ribbons\` field. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: No #### Currency (currency) **Description**: The product's currency. Because stores only have one currency, this value is the same for all products. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price (price) **Description**: The product's price. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Discounted Price (discountedPrice) **Description**: The discounted price, if any. If no discount is configured, the full price is displayed. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Formatted Price (formattedPrice) **Description**: The price formatted with the currency (e.g., $20). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Formatted Discounted Price (formattedDiscountedPrice) **Description**: The discounted price formatted with the currency (e.g., $15). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price Per Unit (pricePerUnit) **Description**: The product's base price per unit, automatically calculated based on provided pricePerUnitData (e.g., 0.3). **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Formatted Price Per Unit (formattedPricePerUnit) **Description**: The product's base price per unit, formatted with the currency (e.g., $0.30) **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price Per Unit Data (pricePerUnitData) **Description**: Price per unit quantities and measurement units. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: No ```javascript { "totalQuantity": 1, "totalMeasurementUnit": "KG", "baseQuantity": 100, "baseMeasurementUnit": "G" } ``` #### Track Inventory (trackInventory) **Description**: Indicates whether inventory is tracked for this product. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### In Stock (inStock) **Description**: Indicates whether the product is in stock. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Quantity In Stock (quantityInStock) **Description**: The number of items currently in stock. Only relevant for products whose "TrackInventory" field is "True". **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Additional Info Sections (additionalInfoSections) **Description**: Displays the Additional Info sections you have in your store in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```javascript [ { "title": "PRODUCT INFO", "description": "I'm a product detail." }, ... ] ``` #### Product Options (productOptions) **Description**: Displays the defined product options and choices as a complex JSON object. Also supports easy querying and filtering. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes You can query the Products collection and filter datasets connected to the collection based on the product options you have defined in your store. The product options are stored in a complex JSON object, with the choices for each option stored as an array of objects (see below). Rather than searching through each choice, you can use special syntax that only requires the product option name and the values of choices available for that option. You can filter this field using these functions: eq, ne, hasSome, hasAll. For example, if you have a product option called "Size," you can use `productOptions.size.value` in your code (note that the case of the name doesn't matter). If you have a product option called "Color," you use `productOptions.color.value`. You can also use any custom options you may have defined. The name you use is the name of the option in your store. ```javascript // filter a dataset by color import wixData from 'wix-data'; $w('#myDataset').setFilter(wixData.filter() .hasSome('productOptions.color.value', ['#0000ff', ...]) ) ``` ```javascript // query the "Stores/Products" collection by color import wixData from 'wix-data'; wixData.query('Stores/Products') .hasSome('productOptions.color.value', ['#0000ff', ...]) .find() .then(console.log) ``` Format of the JSON object ```javascript { "Option1": { "optionType": "color", "name": "Option1", "choices": [ { "value": "#0000ff", "mainMedia": "missing-media.png", "mediaItems": [], "inStock": true, "visible": true } ] ... }, "Option2": { "optionType": "drop_down", "name": "Size", "choices": [ { "value": "Large", "mainMedia": "wix:image://v1/anImage.jpg/file.jpg#originWidth=1000&originHeight=1000", "mediaItems": [ { "id": "anImage.jpg", "src": "wix:image://v1/anImage.jpg/file.jpg#originWidth=1000&originHeight=1000", "description": "", "title": "title", "type": "Image" }, ... ] "inStock": true, "visible": true }, ... ] } } ``` #### Product Page Url (productPageUrl) **Description**: Displays the URL for the product's page on your site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Manage Variants (manageVariants) **Description**: Indicates whether product variants are managed for this store. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Custom Text Fields (customTextFields) **Description**: Displays any custom fields you may have set up in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```javascript [ { "title": "custom text", "maxLength": 500, "mandatory": true } ... ] ``` #### Product Type (productType) **Description**:The type of product (physical or digital). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Slug (slug) **Description**: The product's slug, which is its URL-friendly name that is unique across the store. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Weight (weight) **Description**: The product's weight. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Collections (collections) **Description**: The collections the product belongs to. **Type**: Reference (Multiple Items) **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: hasSome **Read-only**: Yes #### Inventory Item (inventoryItemId) **Description**: The item's ID in the Stores/InventoryItems collection that corresponds to the product. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Discount (discount) **Description**: Discount deducted from the product's original price. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: No **Note:** "type" is one of the following: - "NONE": Pass this value to remove a discount. - "AMOUNT": Discount amount. In the example below, the discount is 5 ($ / € / £ / ¥ / etc.), depending on the [product's currency](https://support.wix.com/en/article/corvid-wix-stores-products-collection-fields#currency-currency). - "PERCENT": Discount is a percentage. ```javascript { "type": "AMOUNT", "value": 5 } ``` #### SEO Data (seoData) **Description**: Lists any custom SEO data for the product as an array of SEO tag JSON objects. [Learn more.](https://support.wix.com/en/search?term=seo) **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: No ```javascript { "tags": [ { "type": "title", "children": "Indonesian Blend", "custom": false, "disabled": false }, { "type": "meta", "props": { "name": "description", "content": "Half Java Arabica, half Bali Robusta - big flavor with a biggish kick." }, "children": "", "custom": false, "disabled": false }, { "type": "meta", "props": { "name": "robots", "content": "index" }, "children": "", "custom": false, "disabled": false } ... ] } ``` #### Brand (brand) **Description**: Product brand. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt # Method name: find(options: WixDataQueryOptions) # Method package: wixData # Method menu location: wixData --> WixDataQuery --> find # Method Link: https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/find.md # Method Description: Returns the items that match the query. The `find()` function returns a Promise that resolves to the results found by the query and some information about the results. The Promise is rejected if `find()` is called with incorrect permissions or if any of the functions used to refine the query is invalid. Calling the `find()` function triggers the [`beforeQuery()`](wix-data.Hooks.html#beforeQuery) and [`afterQuery()`](wix-data.Hooks.html#afterQuery) hooks if they have been defined. Use the `options` parameter to override default preferences: + Override permission checks with `suppressAuth`. + Ensure the most up-to-date data is retrieved with `consistentRead`. + Prevent hooks from running with `suppressHooks`. + Speed up execution with `omitTotalCount`, if you don't need a count of items matching the query. If you build a query and don't refine it with any `wixDataQuery` functions, `find()` returns the entire collection. > **Notes:**: > - Calling `find()` triggers hooks for the specified collection only. It doesn’t trigger hooks for referenced collections. > - `find()` returns the full items that match the query. To specify which fields to return for each retrieved item, use the `fields()` method. # Method Code Examples: *** Note: do not assume any prop names or enum values other than the ones in the example. ## Perform a find on a query ```javascript query.find() .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query and run it ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .find() .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query, add functions to the query, and run it ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .eq("status", "active") .gt("age", 25) .ascending("last_name", "first_name") .find() .then((results) => { if (results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query with options and run it ```javascript import wixData from 'wix-data'; // ... let options = {     "suppressAuth": true,     "suppressHooks": true }; wixData.query("myCollection") .find(options) .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` --- --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt # Method name: count(options: WixDataOptions) # Method package: wixData # Method menu location: wixData --> WixDataQuery --> count # Method Link: https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/count.md # Method Description: Returns the number of items that match the query. The `count()` function returns a Promise that resolves to the number of items that match the query. The Promise is rejected if `count()` is called with incorrect permissions or if any of the functions used to refine the query is invalid. Calling the `count()` function triggers the [`beforeCount()`](wix-data.Hooks.html#beforeCount) and [`afterCount()`](wix-data.Hooks.html#afterCount) hooks if they have been defined. Use the `options` parameter to run `count()` without checking for permissions or without its registered hooks. Any function that does not filter query results (e.g., [`ascending()`](#ascending)) does not affect the result of `count()`. If you build a query and don't refine it with any `WixDataQuery` functions, `count()` returns the total number of items in the collection. If you have already run a query with [`find()`](#find), you can retrieve the number of query results without calling `count()`. The [`find()`](#find) function returns a Promise that resolves to a [`WixDataQueryResult`](https://dev.wix.com/docs/velo/api-reference/wix-data/wix-data-query-result/introduction.md) object, which has a `totalCount` property whose value is the number of results. # Method Code Examples: *** Note: do not assume any prop names or enum values other than the ones in the example. ## Perform a count on a query ```javascript query.count() .then((num) => { let numberOfItems = num; }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query and perform a count on it ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .count() .then((num) => { let numberOfItems = num; }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query and perform a count on it ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .eq("status", "active") .gt("age", 25) .count() .then((num) => { let numberOfItems = num; }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` --- --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt # Method name: distinct(propertyName: string, options: WixDataQueryOptions) # Method package: wixData # Method menu location: wixData --> WixDataQuery --> distinct # Method Link: https://dev.wix.com/docs/velo/apis/wix-data/wix-data-query/distinct.md # Method Description: Returns the distinct values that match the query, without duplicates. The `distinct()` function returns a Promise that resolves to: + The distinct values found in the specified field when running the query. + Additional information about the results, such as the number of values that match the query. Unlike `find()`, which returns all item objects that match the query, `distinct()` returns matching field values, and eliminates duplicate field values from the query result. You cannot use `find()` and `distinct()` together. For an item to be resolved as distinct, only the specified field must be distinct. Other fields for that item in the collection are not evaluated when resolving the promise. The Promise is rejected if `distinct()` is called with incorrect permissions or if any of the functions used to refine the query is invalid. > **Notes:** > - Only site visitors with [Data Read](https://support.wix.com/en/article/collection-permissions-an-overview#permissions) permissions can retrieve and view data. You can override the permissions by setting the `suppressAuth` option to `true`. - `distinct()` returns the full distinct items that match the query. To specify which fields to return for each retrieved item, use the `fields()` method. # Method Code Examples: *** Note: do not assume any prop names or enum values other than the ones in the example. ## Get distinct values from a query ```javascript query.distinct("state") .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a distinct query and run it ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .distinct("state") .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a query, add functions to the query, and find distinct values ```javascript import wixData from 'wix-data'; // ... wixData.query("myCollection") .eq("status", "active") .gt("age", 25) .distinct("state") .then((results) => { if (results.items.length > 0) { let items = results.items; let firstItem = items[0]; let totalCount = results.totalCount; let pageSize = results.pageSize; let currentPage = results.currentPage; let totalPages = results.totalPages; let hasNext = results.hasNext(); let hasPrev = results.hasPrev(); let length = results.length; let query = results.query; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` ## Create a distinct query with options and run it ```javascript import wixData from 'wix-data'; // ... let options = { "suppressAuth": true }; wixData.query("myCollection") .distinct("state", options) .then((results) => { if(results.items.length > 0) { let items = results.items; let firstItem = items[0]; } else { // handle case where no matching items found } }) .catch((error) => { let errorMsg = error.message; let code = error.code; }); ``` --- --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Working with Wix App Collection Fields ## Article: Working with Wix App Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/working-with-wix-app-collection-fields.md ## Article Content: # Working with Wix App Collection Fields [Wix app collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) are special collections added by some [apps made by Wix](https://dev.wix.com/docs/develop-websites/articles/wix-apps/about-apps-made-by-wix.md) when they are installed on a site. These collections have fixed permissions that cannot be changed. The fields in each collection support different data query and site integration functionalities, such as [sorting](https://dev.wix.com/docs/velo/api-reference/wix-data/wix-data-sort/introduction.md), [filtering](https://dev.wix.com/docs/velo/api-reference/wix-data/wix-data-filter/introduction.md), or [connecting the collection data to a site](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-on-your-page). When working with a Wix app collection, check whether its fields support the action you want to perform. You can find information about each Wix app collection in the articles below. The following table lists the potential functionalities each field might support: | Functionality | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Type | [Field type](https://support.wix.com/en/article/cms-formerly-content-manager-supported-content-in-collection-field-types). | | Connecting to data | Whether the field can be used to [display data on the site](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-on-your-page). | | Used in a dynamic URL | Whether the field can be used in a [dynamic page URL](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/dynamic-pages/making-dynamic-page-urls-meaningful-with-prefixes.md). | | Sorting | Whether the field can be used to [sort](https://support.wix.com/en/article/cms-formerly-content-manager-hiding-sorting-and-filtering-fields-in-your-collection-view#sorting-items-in-your-collection) the collection. | | Filtering | Whether the field can be used to [filter](https://support.wix.com/en/article/cms-formerly-content-manager-hiding-sorting-and-filtering-fields-in-your-collection-view#adding-filters-to-fields-in-your-collection-views) the collection. If yes, the [filter methods](https://dev.wix.com/docs/velo/api-reference/wix-data/wix-data-filter/introduction.md) that can be called on the fields. | | Read-only | Whether the field is read-only. Some fields cannot be edited or managed from the collection. You can only update these fields from the relevant app in the [site's dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). | ## See also - [About apps made by Wix](https://dev.wix.com/docs/develop-websites/articles/wix-apps/about-apps-made-by-wix.md) - [Working with Wix app collections and code](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) - [Querying Wix app collections](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/querying-wix-app-collections.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Bookings "Services" Collection Fields ## Article: Wix Bookings "Services" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-services-collection-fields.md ## Article Content: # Velo: Wix Bookings "Services" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Services collection in code, refer to it as `"Bookings/Services"`. ```javascript wixData.query("Bookings/Services") .find() .then( (results) => { // handle the results } ); ``` This document describes the properties and fields in your Services collection. ### Permissions The Services collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Services collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.
**Deprecation Notice:** As of January 31, 2025, Wix Bookings [integrates with Wix Forms APIs](https://dev.wix.com/docs/api-reference/business-solutions/bookings/wix-forms-integration.md), which affects how you can manage booking form data. This integration means the `form` fields aren't populated for services created after the integration. Services created before the integration also lose this data when they're updated. To reliably manage booking form data, follow [these sample flows](https://dev.wix.com/docs/api-reference/business-solutions/bookings/wix-forms-integration?apiView=SDK.md#sample-flows).
#### ID (\_id) **Description**: The service ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Name (serviceName) **Description**: The name of the service. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Type (serviceType) **Description**: The type of service offered (Appointment, Class, Course). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Image (imageURL) **Description**: Displays the photo for this service as it appears in the Booking Manager. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Description (description) **Description**: The service description. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Tagline (tagLine) **Description**: The service tagline. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: The service's slug, which is its URL-friendly name that is unique across the site. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Service Page URL (servicePageURL) **Description**: The link to the service's page on your site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Booking Flow Entry URL (bookingFlowEntryURL) **Description**: The link to the service's calendar page for appointments & classes, or to the service's page for courses. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price Summary (priceSummary) **Description**: The price summary, as follows: * For paid bookings, displays "Amount + Currency". * For free bookings, displays "Free" or any custom text specified. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Payment Type (paymentType) **Description**: The payment method defined for this service. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Part of Pricing Plan (partOfPricingPlan) **Description**: Indicates whether the service is associated with a pricing plan, such as a membership deal. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price Amount (priceAmount) **Description**: The cost of a single session of this service. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Deposit Amount (depositAmount) **Description**: The deposit required to book this service, if any. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Currency (currency) **Description**: The currency for this service. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Max Session Capacity (maxSessionCapacity) **Description**: The maximum number of participants that can be booked for a single session of this service. For appointments, this value is always one. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Max Number Of Participants Per Reservation (maxNumberOfParticipantsPerReservation) **Description**: The number of participants a single customer can sign up for one session of this service. For example, you can define that one customer can book up to 3 participants for a given session. For appointments, this value is always one. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No #### Form Fields (form) **Description**: List of additional values needed for booking a session of the service.
**Breaking change:** This field isn't populated for services created after the [Wix Forms integration](https://dev.wix.com/docs/api-reference/business-solutions/bookings/wix-forms-integration.md). Services created before the integration may still have this data, but it is lost when the service is updated. To reliably manage booking form data, follow [these sample flows](https://dev.wix.com/docs/api-reference/business-solutions/bookings/wix-forms-integration?apiView=SDK.md#sample-flows).
**Type**: Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "fields":[ { "id":"20657271-c55f-43d6-adfd-39b7acc38e11", "type":"text", "label":"Name", "constraints":{ "required":true } }, { "id":"87edd4e0-42b1-4802-8766-584f3eeb6436", "type":"email", "label":"Email", "constraints":{ "required":true } }, { "id":"78bdd186-648e-420d-b8c7-59ebbadb6a4b", "type":"tel", "label":"Phone Number", "constraints":{ "required":false } }, { "id":"2f37d76d-3462-466f-b1f2-e05ebf1889ad", "type":"text", "label":"Add Your Message", "constraints":{ "required":false } }, { "id":"61997cef-9fa2-4f67-9773-67ade57f8754", "type":"text", "label":"Street", "constraints":{ "required":true } }, { "id":"1a4d2b86-bb03-4f3a-ac18-3055e284066b", "type":"text", "label":"Apt. / Floor No.", "constraints":{ "required":false } }, { "id":"cf34b269-ddd7-4290-aa2e-5b535f5e9fbe", "type":"text", "label":"City", "constraints":{ "required":true } } ] } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Bookings "Schedule" Collection Fields ## Article: Wix Bookings "Schedule" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-schedule-collection-fields.md ## Article Content: # Velo: Wix Bookings "Schedule" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Schedule collection in code, refer to it as `"Bookings/Schedule"`. ```javascript wixData.query("Bookings/Schedule") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Schedule collection.  ### Permissions The Schedule collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Schedule collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. >**Note** > You cannot create a dynamic page based on the Schedule collection. #### ID (\_id) **Description**: Displays the service name from the Services collection. **Type**: Reference **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Service Schedule (serviceSchedule) **Description**: A JSON object that represents the schedule that has been defined for this service. **Type**: Reference **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "mon": [ { "staffid": "7b14c662-ef34-439a-45a6-8g43b8e15ba7", "startTime": "10:00:00:000", "endTime": "11:30:00:000" } ... ], "tue": [], ... } ``` #### Service Duration in Minutes (serviceDurationInMinutes) **Description**: The duration of this service, in minutes. For example, if your class lasts an hour and a half, this field displays 90. **Type**: Number **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Repeats Every X Weeks (repeatsEveryXWeeks) **Description**: How often this service recurs. For example, if the sessions repeat every 2 weeks, this field displays 2. **Type**: Number **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Bookings "Staff" Collection Fields ## Article: Wix Bookings "Staff" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-staff-collection-fields.md ## Article Content: # Velo: Wix Bookings "Staff" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Staff collection in code, refer to it as `"Bookings/Staff"`. ```javascript wixData.query("Bookings/Staff") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Staff collection. ### Permissions The Staff collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Staff collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. >**Note** > You cannot create a dynamic page based on the Staff collection. #### ID (\_id) **Description**: Unique staff member ID. **Type**: Text **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Staff Name (name) **Description**: The staff member's name. **Type**: Text **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Staff Image (image) **Description**: The staff member's image. **Type**: Image **Can connect to data**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Ideas for Working with Your Wix Bookings App Collections ## Article: Ideas for Working with Your Wix Bookings App Collections ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/ideas-for-working-with-your-wix-bookings-app-collections.md ## Article Content: # Velo: Ideas for Working with Your Wix Bookings App Collections >**Note:** > This tutorial and its steps are based on a Wix Editor site. You can adapt the steps for a Wix Studio site by using the equivalent [Wix Studio features](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/about-coding-with-wix-studio.md). This article presents some ideas for how you can enhance your Bookings site with Velo. Throughout this article we refer to [this site](https://www.urbannomadoregon.com/) to illustrate the process. It is a travel site that offers tours in different locations. It features a page that lists all the services at each location as well as a customized booking page for each service. Specifically, we'll describe: * The collections we created and how we connected them to our Wix Bookings App [**Services**](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-services-collection-fields.md) collection. * The customized pages we designed to showcase our services using information from our collections, and how we linked them to existing pages from the Bookings app. * The form we built to get reviews from our customers and how we displayed those reviews on our page. Most of the ideas in this article do not require any code. The page that displays the reviews and the form that lets visitors add reviews, however, do require some simple code that we include here in the article. You'll be able to copy and paste the code onto your page and make some minor adjustments to get it to work. ## New Collections The first thing we did was to create new collections to store the information we wanted to showcase on our site. We used [reference fields](https://support.wix.com/en/article/cms-creating-multi-reference-fields) to connect our new collections to the **Services** collection that comes from the Wix Bookings app. Later in this article, you'll see how we filtered datasets based on these reference fields so that our pages display only relevant information each time. #### Locations collection This collection stores the locations where our services take place. It includes images, various descriptions, and a field for the dynamic page URL. We'll use this collection to create a dynamic item page for our locations.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/11/ca3bf6c4-d3f3-4e1b-a7cb-a19a0085c3d5.png)
#### Service\_Locations collection This collection maps each of our services to its location using two reference fields, one pointing to the **Services** collection and the other to the **Locations** collection we just created. Later in this article, you'll see how we used this collection when we created the Locations dynamic item page to display the services available at each location.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/13/db7dfdc8-a7ca-4cb8-ae19-f80a2ec562e6.png)
#### Extra\_Service\_Info collection This collection contains additional information about each of our services, including the pickup point, target audience, time of day, and additional images. Here again we used a reference field to connect this information to each service in the **Services** collection. Later in this article we'll use this collection when we create our [customized booking page](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/ideas-for-working-with-your-wix-bookings-app-collections.md).
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/11/1785acfa-d236-41bb-80df-e35501bdbd94.png)
#### Service\_Reviews collection This collection stores the reviews left by users for each service. This collection also has a reference field pointing to the **Services** collection as well as fields for storing the date the review was submitted, text of the review and information about the reviewer. We made sure to set this [collection's permissions](https://support.wix.com/en/article/about-collection-permissions) so that visitors can create content for it.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/11/8c811de0-fbe2-4f87-bcd4-636391073e11.png)
## Master/Detail Pages Our demo site has 2 master/detail pages, both of which are also dynamic item pages. Both pages use multiple datasets that are filtered by reference fields. See [this article](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md) for more information. * The first page is connected to the **Locations** collection and displays information about our locations and the services offered at each one.  * The second page is a customized booking form connected to the **Services** collection. It lists additional information for each service as well as user feedback for each service.  ### Locations Page with Details about the Services To view this page, from the home page of the [demo site](https://www.urbannomadoregon.com/), click the "[Explore](https://www.urbannomadoregon.com/Locations/PORTLAND-METRO-AREA)" button in the "Portland Metro Area" item. This page you are now viewing is a dynamic item page connected to the **Locations** collection. Note how in addition to displaying information about the Portland location, it also lists the services offered there. Let's see how we set this page up. 1. Starting from our **Locations** collection, we created and designed the dynamic item page, using text elements and images connected to the fields in the collection. 2. We added a second dataset to the page, connected it to the **Service\_Locations** collection, and then filtered it by its "Location" reference field. This causes the dataset to only display those items whose location matches the current location on our dynamic page, in this case Portland Metro Area. (See [here](https://support.wix.com/en/article/cms-formerly-content-manager-about-filtering-and-sorting-live-site-content-with-datasets#filter-datasets-for-clarity) for details on how to set up this filter.) 3. We connected a repeater to this filtered dataset. Because the reference field in the **Services\_Locations** collection points to the **Services** collection, we can connect elements to fields in the **Services** collection. This is true even though our page doesn't have a dataset connected directly to that collection. In other words, the image, service name, and price information in the repeater come directly from the information we added in our Bookings app.  4. The "Book It" button in the repeater links to our [customized booking page](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/ideas-for-working-with-your-wix-bookings-app-collections.md).  This is what our page looks like in the Editor. Elements connected to the "Locations" dataset are marked in red. The repeater and all its elements are connected to the filtered "Services\_Locations" dataset and are marked in blue. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/12/822bf6d2-4f22-4bc9-81d1-cb26a996aebb.png)
### Customized Booking Page with Feedback From the Portland Metro Area page, click the "[Book It](https://www.urbannomadoregon.com/Bookings/Services/food-cart-tastings-1)" button under "Food Cart Tastings" to go to the customized booking page for that service. This is also a master/detail dynamic item page, but with a twist: it displays 2 sets of details * The first set of details is the additional information displayed at the top of the page. * The second set of details is the reviews displayed at the bottom of the page. #### Additional Service Information Details The top part of the Services dynamic item page displays the information we stored in the **Extra\_Service\_Info** collection. We built it much the same way as the previous page. 1. Starting from the Wix App **Services** collection, we created and designed the dynamic item page. The "Book It" button is linked to the "Booking Flow Entry URL" field in the **Services** collection. This means that when our visitors click "Book It," they end up at our regular Wix Bookings App calendar page. 2. We added another dataset to the page and connected it to the **Extra\_Service\_Info** collection. We filtered this dataset by its "Service" reference field. Then we connected elements to this filtered dataset. The elements under "When," "For Who," and "Pickup" are connected directly to our **Extra\_Service\_Info** collection, as are the images at the bottom of the page. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/b0387850-90c2-4f00-a761-a0c5104e1c8c/2018/06/13/4f36d2d5-6f3f-46b4-8744-2588d7e1676c.png) #### Review Display Section The review section of the customized booking page is connected to the **Service\_Reviews** collection. It displays the reviews received for this service and a form for getting reviews. This part of the page requires some code to make it work. 1. We added a third dataset to the page, connected it to the **Service\_Reviews** collection, and then filtered it by its "Service" reference field. Because we will end up adding 2 datasets that point to the same collection, we renamed this first one to "Display Reviews" to make it meaningful. 2. In order to display the total number of reviews we've received, we added a text element to our page and wrote the following code to get the total count and then display it in the element. ```javascript $w("#displayReviews").onReady(() => { let count = $w("#displayReviews").getTotalCount(); $w('#reviewsCount').text = (count + " Reviews") }); ``` >**If you want to use this code on your site, you’ll need to make these substitutions:** > * '#displayReviews': the ID of the dataset > * '#reviewsCount': the ID of the text element that displays the number of reviews 1. We connected a repeater to this dataset, attached text elements to it, and connected them to the same dataset in order to display the text of the review and name of the reviewer.  2. In order to display a formatted version of the date, we attached a text element to the repeater and used the following code.  ```javascript $w('#reviewRepeater').onItemReady(($item, itemData, index) => { $item('#dateDisplay').text = itemData.review_date.toLocaleDateString("en-US"); }) ``` >**If you want to use this code on your site, you’ll need to make these substitutions:** > * '#reviewRepeater': the ID of the repeater > * '#dateDisplay': the ID of the text element that displays the date > * review\_date: the field ID of the date field in the collection 1. The "Book It" button links to the Wix Bookings App calendar page. In its Connect Panel, it appears as the "Booking Flow Entry URL" dynamic page. ## Form for Submitting Reviews At the bottom of the customized booking page is a form where visitors can submit reviews for the service being displayed on the dynamic item page. This is how we created it. 1. We added a 4th dataset to the page, connected it to the **Service\_Reviews** collection, and set its mode to "Write-only." We called it "Get reviews" to differentiate it from the "Display reviews" dataset we added in the previous section. 2. We added 3 input elements and connected them to the appropriate fields in the dataset. We also added a submit button and configured it to remain on this page after submit. 3. We wrote the following code to include today's date and the service being reviewed in the data being saved to the collection. ```javascript $w('#getReviews').onBeforeSave(() => { $w('#getReviews').setFieldValue('service', $w('#dynamicDataset').getCurrentItem()._id); $w('#getReviews').setFieldValue('review_date', new Date()); }) ``` >**If you want to use this code on your site, you’ll need to make these substitutions:** > * '#getReviews': the ID of the dataset connected to the Services\_Reviews collection > * 'service': the field ID of the reference field that points to the Services collection > * '#dynamicDataset': the ID of the dynamic dataset > * 'review\_date': the field ID of the date field in the Services\_Reviews collection 1. We added the following code to refresh the dataset that is displaying the reviews after the new item is saved to the collection. This means that the new review will appear on the page after it has been submitted. ```javascript $w('#getReviews').onAfterSave(() => { $w('displayReviews').refresh(); }) ``` >**If you want to use this code on your site, you’ll need to make this substitution:** > * '#getReviews': the ID of the dataset --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Bookings Schema for Wix Search ## Article: Wix Bookings Schema for Wix Search ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/wix-bookings-schema-for-wix-search.md ## Article Content: # Wix Bookings Schema for Wix Search This article describes the Wix Booking fields you can [search](https://www.wix.com/velo/reference/wix-search.html#search), [sort](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#ascending), [filter](https://www.wix.com/velo/reference/wix-search.html#filter), and [apply facets](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#facets) to when using the [wix-search API](https://www.wix.com/velo/reference/wix-search.html). To search Wix Booking services on your site, set the search document type as `"Bookings/Services"`: ``` wixSearch.search(phrase) .documentType("Bookings/Services") .find() .then( (results) => { // handle the results } ); ``` **Notes** The Bookings fields supported by wix-search are not identical to the fields in your site's [Bookings/Services collection](https://support.wix.com/en/article/velo-wix-bookings-services-collection-fields). The following fields appear in each matching search result [document](https://www.wix.com/velo/reference/wix-search.WixSearchResult.html#documents) when searching Wix Booking services: ## documentType **Description**: Document type that was searched. In this case, "Bookings/Services". **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## \_id **Description**: Service ID. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## title **Description**: Service name. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## description **Description**: Service description. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## url **Description**: Service page's link. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## image **Description**: File source of the image for this service. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## category **Description**: Category associated with the service. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## tagLine **Description**: Service's tagline (subtitle). **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## staffMembers **Description**: Staff members associated with the service. **Type**: Array of Strings **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: No **Can filter**: hasSome, hasAll --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Custom Booking Calendar Page ## Article: Build a custom Booking Calendar page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/build-a-custom-booking-calendar-page.md ## Article Content: # Velo: Build a Custom Booking Calendar Page You can replace your site’s default [Wix Booking Calendar page](https://support.wix.com/en/article/wix-bookings-customizing-your-calendar-page) with your own customized version. This enables you to modify or extend the page’s functionality to suit your precise business needs. > **Important:** This feature is currently not supported with [Multi-service bookings appointments](https://support.wix.com/en/article/wix-bookings-scheduling-multi-service-appointments). ![bookings-flow](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_bookings-flow.png) To integrate the page into your [site’s booking flow](https://support.wix.com/en/article/about-wix-bookings-pages) and implement its functionality, you need to add code to the page and use Velo APIs. Your code has to do several things: * Get data about the selected service and query session availability data. * Handle UI initialization and interactions. * Implement any customized business logic. * Direct users to the next page in the flow. ## Step 1 | Add a custom Calendar page to your site To create a custom calendar page, do one of the following:
Wix Studio 1. Click ![code-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_code-studio-icon.png) and then **Start Coding** from the sidebar on the left side of the editor. 2. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_pages-icon-studio.png) on the sidebar. 3. On the **Booking Calendar** (under **Bookings Pages**), click the More Actions icon ![more-actions-studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_more-actions-studio.png). 4. Click **Replace with custom page**. 5. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_replace-bookings-page-studio.png)
Wix Editor 1. Turn on [Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site). 2. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_pages-icon.png) on the left side of the editor. 3. Click **Bookings Pages**, and then on the **Booking Calendar** page, click ![ellipsis-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_ellipsis-icon.png). 4. Click **Replace with custom page**. 5. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-booking-calendar-md_velo-articles_wix-bookings-with-velo_images_replace-bookings-calendar-classic.png)
## Step 2 | Design your custom page [Add elements to the page](https://support.wix.com/en/article/wix-editor-adding-and-deleting-elements) to create your business’s customized design and functionality, such as text, buttons, and images. At a minimum, these elements must: * Display information about the selected service. * Provide the site visitor with a way to view availability and select a slot. * Provide an action button that navigates to the next page in the flow (usually the Booking Form). ## Step 3 | Add Velo code to your page 1. Use the [`getAppPageData()`](https://www.wix.com/velo/reference/wix-window-frontend/getapppagedata) function to get the [service object](https://www.wix.com/velo/reference/wix-window-frontend/app-page-data#wix-window-frontend_app-page-data_booking-calendar-page) associated with the page. For example: ``` ts import wixWindowFrontend from 'wix-window-frontend'; import { availabilityCalendar } from 'wix-bookings.v2'; import wixLocation from 'wix-location'; $w.onReady(async function () { const pageData = await wixWindowFrontend.getAppPageData(); const serviceId = pageData.service.id; }); ``` > **Note**: To receive a populated page data object using `getAppPageData()` when testing your code, do the following: > > * Create at least one Bookings service. > * Preview the Bookings Calendar page and then return to the editor. 2. Get the service’s session availability data using the [`availabilityCalendar.queryAvailability()`](https://www.wix.com/velo/reference/wix-bookings-v2/availabilitycalendar/queryavailability) function. The following example queries data for the upcoming week: ```ts const today = new Date(); const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000); let query = { filter: { serviceId: [serviceId], startDate: today, endDate: nextWeek, } } const selectedTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const options = { timezone: selectedTimezone }; const availability = await availabilityCalendar.queryAvailability(query, options); const entries = availability.availabilityEntries; ``` 3. Initialize your custom page’s elements with the service information and availability data that you retrieved in the previous steps. Use the [`$w` namespace](https://www.wix.com/velo/reference/w) to control the page’s elements. For example: ```ts $w('#serviceName').text = myService.name; const slotOptions = []; entries.forEach((entry, index) => { const label = `${entry.slot.startDate}-${entry.slot.endDate}`; const value = `${index}``; slotOptions.push({ label, value }); }); $w('#dropdown').options = slotOptions; ``` 4. Depending on the functionality you’re developing, implement any business logic that your customized page requires. 5. Add an [event handler](https://support.wix.com/en/article/velo-reacting-to-user-actions-using-events) to the page’s action button so that it navigates to the next page in the flow, which is typically your site’s Booking Form. You can [create a link to a preloaded Booking Form](https://www.wix.com/velo/reference/wix-bookings/shareable-booking-form-links), specifying the appropriate query params based on the user's selections. For example: ```ts $w('#nextButton').onClick(() => { const selectedIndex = $w('#dropdown').selectedIndex; const selectedSlot = entries[selectedIndex].slot; let queryParams = `?bookings_serviceId=${serviceId}&bookings_resourceId=${selectedSlot.resource._id}&bookings_startDate=${encodeURIComponent(selectedSlot.startDate)}&bookings_endDate=${encodeURIComponent(selectedSlot.endDate)}&bookings_timezone=${selectedTimezone}`; const nextUrl = "/booking-form" + queryParams; wixLocation.to(nextUrl); }) ``` Your custom page can now display the service’s data, provide site visitors with an interface for selecting an available slot, and direct them to the next page in the booking flow. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Custom Booking Service Page ## Article: Build a custom Booking Service page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-bookings/build-a-custom-booking-service-page.md ## Article Content: # Velo: Build a Custom Booking Service Page You can replace your site’s default [Wix Bookings Service page](https://support.wix.com/en/article/wix-bookings-customizing-your-service-pages) with your own customized version. This enables you to modify or extend the page’s functionality to suit your precise business needs To integrate the page into your [site’s booking flow](https://support.wix.com/en/article/about-wix-bookings-pages) and implement its functionality, you need to add code to the page and use Velo APIs. Your code has to do several things: * Get data about the selected service. * Handle UI initialization and interactions. * Implement any customized business logic. * Direct users to the next page in the flow. ## Step 1 | Add a custom Service page to your site To create a custom Service page, do one of the following:
Wix Studio 1. Clicking ![code-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_code-studio-icon.png) and then **Start Coding** from the sidebar on the left side of the editor. 2. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_pages-icon-studio.png) on the sidebar. 3. On the **Service Page** (under **Bookings Pages**), click the More Actions icon ![more-actions-studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_more-actions-studio.png). 4. Click **Replace with custom page**. 5. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_replace-bookings-page-studio.png)
Wix Editor 1. Turn on [Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site). 2. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_pages-icon.png) on the left side of the editor. 3. Click **Bookings Pages**, and then on the **Service Page** page, click ![ellipsis-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_ellipsis-icon.png). 4. Click **Replace with custom page**. 5. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/custom-service-page-md_velo-articles_wix-bookings-with-velo_images_replace-bookings-page-wix-editor.png)
## Step 2 | Design your custom page [Add elements to the page](https://support.wix.com/en/article/wix-editor-adding-and-deleting-elements) to create your business’s customized design and functionality, such as text, buttons, and images. At a minimum, these elements must: * Display information about the selected service. * Provide an action button that navigates to the next page in the flow (usually the Booking Calendar). ## Step 3 | Add Velo code to your page 1. Use the [`getAppPageData()`](https://www.wix.com/velo/reference/wix-window-frontend/getapppagedata) function to get the [service object](https://www.wix.com/velo/reference/wix-window-frontend/app-page-data#wix-window-frontend_app-page-data_booking-service-page) associated with the page. For example: ``` ts import wixWindowFrontend from 'wix-window-frontend'; import wixLocation from 'wix-location'; $w.onReady(async function () { const pageData = await wixWindowFrontend.getAppPageData(); const serviceName = pageData.service.name; $w('#text1').text = serviceName; }); ``` > **Note**: To receive a populated page data object using `getAppPageData()` when testing your code, do the following: > > * Create at least one Bookings service. > * Preview the Service page and then return to the editor. 2. Depending on the functionality you’re developing, implement any business logic that your customized page requires. 3. Add an [event handler](https://support.wix.com/en/article/velo-reacting-to-user-actions-using-events) to the page’s action button so that it navigates to the next page in the flow, which is typically your site’s Booking Calendar. You can [create a link to a Booking Calendar](https://www.wix.com/velo/reference/wix-bookings/shareable-booking-form-links) with preset values. > **Note**: Courses do not need a Booking Calendar. Their service page should [navigate directly to the Booking Form](https://www.wix.com/velo/reference/wix-bookings-frontend/shareable-booking-form-links). For example: ```ts $w('#nextButton').onClick(() => { const serviceSlug = pageData.service.supportedSlugs[0].name; const nextUrl = `/booking-calendar/${serviceSlug}`; wixLocation.to(nextUrl); }) ``` Your custom page can now display the service’s data and direct site visitors to the next page in the booking flow. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "Collections" Collection Fields ## Article: Wix Stores "Collections" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-collections-collection-fields.md ## Article Content: # Velo: Wix Stores "Collections" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Collections collection in code, refer to it as `"Stores/Collections"`. ```javascript wixData.query("Stores/Collections") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Collections collection.
**Important:** * You can query up to 100 items from the Collections collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The Collections collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Collections collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The collection ID that was created by the server. This is a system field and is hidden by default. This information also appears in the Products collection in a hidden field called "collections.id". You can copy the ID from here and then use it to query the Products collection by the collection ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Name (name) **Description**: The name of the collection. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes   **Can be filtered**: Yes **Read-only**: Yes #### Main Media (mainMedia) **Description**: Displays the main media item (image or video) for this collection as it appears in the Store Manager. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "InventoryItems" Collection Fields ## Article: Wix Stores "InventoryItems" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-inventory-items-collection-fields.md ## Article Content: # Velo: Wix Stores "InventoryItems" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the InventoryItems collection in code, refer to it as `"Stores/InventoryItems"`. ```javascript wixData.query("Stores/InventoryItems") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your InventoryItems collection. 
**Important:** * You can query up to 100 items from the InventoryItems collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The InventoryItems collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the InventoryItems collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The inventory item ID (GUID) that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Date and time the inventory item was last changed. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome **Read-only**: Yes #### External ID (externalId) **Description**: An external ID for the inventory ID in some other system. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Product ID (productId) **Description**: Unique identifier of the shopping cart. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Track Quantity (trackQuantity) **Description**: Indicates whether inventory is tracked for this item. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Variants (variants) **Description**: Displays the defined inventory item's variants and its inventory tracking information as a complex JSON object. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes Format of the JSON object ```javascript { "variantId" : "abc-123-456", "inStock" : true, "quantity" : 30 }, { "variantId" : "def-456-789", "inStock" : false }, { "variantId" : "ghi-789-000", "inStock" : true, "quantity" : 1 } ``` ### Related APIs The following APIs can return data from the InventoryItems collection: * [wix-stores-backend](https://www.wix.com/code/reference/wix-stores-backend.html) * [$w.ProductPage](https://www.wix.com/code/reference/$w.ProductPage.html?draft=_#getProduct) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "Orders" Collection Fields ## Article: Wix Stores "Orders" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-orders-collection-fields.md ## Article Content: # Velo: Wix Stores "Orders" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Orders collection in code, refer to it as `"Stores/Orders"`. ```javascript wixData.query("Stores/Orders") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Orders collection.
**Important:** You can query up to 100 items from the Orders collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The Order collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Admin  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Order collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### Number (number) **Description**: Running order number. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, le, lt, gt, ge **Read-only**: Yes #### ID (\_id) **Description**: The ID of the order. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: The date and time the order was last updated. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, le, gt, ge **Read-only**: Yes #### Billing Info (billingInfo) **Description**: Billing information as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "address": { "formatted": "235 W 23rd St, NYC, New York 10011, USA", "city": "NYC", "country": "USA", "addressLine": "235 W 23rd St", "addressLine2": "Apt 3", "postalCode": "10011", "subdivision": "NY" } "firstName": "John", "lastName": "Doe", "email": "john.doe@somedomain.com", "phone": "5555555555", "company" : "Company Name", "vatId": { "number": "452.765.395-39", "type": "CPF" }, "paidDate": "2000-05-24T21:00:00.000Z", "paymentMethod": "VISA", "paymentGatewayTransactionId": "29A06193U6234935D", "paymentProviderTransactionId": "7c03ca74-eaf5-4541-8678-9b857634fdcb" } ``` #### Buyer Info (buyerInfo) **Description**: Buyer information as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note:** "identityType" is one of the following: * "MEMBER": A logged-in site member. * "CONTACT": A Wix contact. ```json { "id": "4kf9ka09-4e9f-a02d-972f-9a5844d9d9a2", "identityType": "CONTACT", "firstName": "John", "lastName":"Doe", "phone": "5555555555", "email": "john.doe@somedomain.com" } ``` #### Buyer Note (buyerNote) **Description**: A note added by the buyer on the cart page when creating the order. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Date Created (\_dateCreated) **Description**: Date and time the order was created. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, le, gt, ge **Read-only**: Yes #### Currency (currency) **Description**: Currency of the order. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Fulfillment Status (fulfillmentStatus) **Description**: The status of the order's fulfillment. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: Value is one of: * "FULFILLED" - Order was fulfilled successfully. * "NOT\_FULFILLED" - Order is not yet fulfilled. * "CANCELLED" - Order was cancelled. * "PARTIALLY\_FULFILLED": Order was partially fulfilled. #### Archived (archived) **Description**: Whether the order is archived. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Line Items (lineItems) **Description**: Lists all the line items for this order in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: The "lineItemType" value is one of: * "PHYSICAL" * "DIGITAL" * "CUSTOM\_AMOUNT\_ITEM" - Item with a custom price. ```json [ { "index": 1, "quantity": 1, "discount": 0, "tax": 5, "taxGroupId": "4", "name": "Black T-Shirt", "translatedName": "Nombre traducido", "productId": "28e0d578-64ac-7c12-b675-d1944d1c4155", "sku": "364215376135191", "weight": 5.2, "lineItemType": "PHYSICAL", "notes": "Some notes here", "variantId": "0042-0005-a316f7c67df7", "fulfillerId": "0000-0420-0005-a3dcfd7e6930", "priceData": { "price": 35, "totalPrice": 40, "taxIncludedInPrice": true }, "options": [ { "option": "Color", "selection": "Red" }, { "option": "Size", "selection": "Small" } ], "customTextFields": [ { "title": "Notes for delivery", "value": "Please leave at front door" } ], "mediaItem": { "altText": "A description of the image", "id": "nsplsh_5386255~mv2_d_2977_3951_s_4_2.jpg", "externalImageUrl": "http://static.wixstatic...al_c,q_90/file.jpg", "src": "wix:image://v1/.../jpg#originWidth=1000&originHeight=1000", "type": "IMAGE" } }, ... ] ``` #### Activities (activities) **Description**: List of activities for this order in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Notes:** The "type" value is one of: * "MERCHANT\_COMMENT" - A merchant comment. * "ORDER\_PLACED" - Order placed. * "ORDER\_PAID" - Order marked as paid, either by the store owner (for offline orders), or when an online transaction is confirmed. * "ORDER\_FULFILLED" - Order shipping status set as fulfilled. * "ORDER\_NOT\_FULFILLED" - Order shipping status set as not fulfilled. * "DOWNLOAD\_LINK\_SENT" - Download link was sent (relevant for orders with digital line items). * "PICKUP\_READY\_EMAIL\_SENT" - Email notification for pickup was sent. * "TRACKING\_NUMBER\_ADDED" - Shipping tracking number was set. * "TRACKING\_NUMBER\_EDITED" - Shipping tracking number was edited. * "TRACKING\_LINK\_WAS\_SET" - Shipping tracking link was set. * "SHIPPING\_CONFIRMATION\_EMAIL\_SENT" - An email confirmation of the order shipment was sent. * "INVOICE\_WAS\_SET" - Invoice was added to the order. * "INVOICE\_WAS\_REMOVED" - Invoice was removed from the order. * "INVOICE\_WAS\_SENT" - Invoice was sent to customer via email. ```json [ { "type": "ORDER_PLACED", "timestamp": "2019-01-28T15:43:28.986Z" }, { "type": "MERCHANT_COMMENT", "author": "jane.doe@somedomain.com", "message": "Enjoy you purchase!", "timestamp": "2019-01-28T15:53:24.347Z" } ] ``` #### Payment Status (paymentStatus) **Description**: Status of the order's payment. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes **Note**: One of:  * "PAID" * "NOT\_PAID" * "PARTIALLY\_REFUNDED" * "FULLY\_REFUNDED" The "NOT\_PAID" status is only for manual payment orders. Other non-paid orders, such as declined payments, do not appear in the orders collection. #### Shipping Info (shippingInfo) **Description**: The order's shipping information as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Notes**:  * Either "shipmentDetails" or "pickupDetails" will be present, but not both. ```json { "deliveryOption": "PPP", "estimatedDeliveryTime": "1 day", "shippingRegion": "Domestic", "shipmentDetails": { "address": { "formatted": "235 W 23rd St, NYC, New York 10011, USA", "city": "NYC", "country": "USA", "addressLine": "235 W 23rd St", "addressLine2": "Apt 3", "postalCode": "10011", "subdivision": "NY" }, "firstName": "John", "lastName": "Doe", "email": "john.doe@somedomain.com", "phoneNumber": "55555555555", "company" : "Some Company", "vatId": { "number": "294.593.590-21", "type": "CPF" }, }, "pickupDetails": { "buyerDetails": { "firstName": "John", "lastName": "Doe", "email": "john.doe@somedomain.com", "phoneNumber": "55555555555" }, "pickupInstructions": "Some instructions", "pickupAddress": { "formatted": "235 W 23rd St, NYC, New York 10011, USA" "city": "New York", "country": "USA", "addressLine": "235 W 23rd St", "postalCode": "10011", "subdivision": "NY" } } } ``` #### Totals (totals) **Description**: The order's total information as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "discount": 0.0, "quantity": 1, "shipping": 0.0, "subtotal": 1.0, "tax": 0.0, "total": 1.0, "weight": 0.0 } ``` #### Weight Unit (weightUnit) **Description**: The unit in which the order's weight is measured ("KG" or "LB"). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Custom Field (customField) **Description**: Information about a custom field added to the checkout process as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "title": "Notes for delivery", "translatedTitle": "Notas de entrega", "value": "Please call when outside" } ``` #### Fulfillments (fulfillments) **Description**: Lists of fulfillments for this order in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json [ { "id": "8", "dateCreated": "2020-04-20T21:00:00.000Z", "lineItems": [ { "index": 1, "quantity": 1 } ], "trackingInfo": { "trackingNumber": "449044304137821", "shippingProvider": "FEDEX", "trackingLink": "https://www.fedex.com/...trackingnumber=449044304137821", } } ] ``` #### Buyer Language (buyerLanguage) **Description**: The site's displayed language. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Cart ID (cartId) **Description**: The shopping cart's unique ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Channel Info (channelInfo) **Description**: Information about the sales channel that submitted the order, as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "externalOrderId": "03-04896-40604", "externalOrderUrl": "http://www.eBay.com/sh/ord/details?orderid=03-04896-40604", "type": "EBAY" } ``` #### Entered By (enteredBy) **Description**: Information about the identity of the order's operator as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: The identityType value - the order was created by one of the following: * "USER" - Wix user who performed a POS transaction on behalf of the buyer. * "MEMBER" - Logged-in site member. * "CONTACT" - A Wix contact. ```json { "id": "f6c2c0f9-4e9f-a58d-a02d-9af2497294d9", "identityType": "MEMBER" } ``` #### Refunds (refunds) **Description**: Information about order refunds in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: Details about externalRefund boolean field: * Indicates whether the refund was made externally. * An external refund refers to refunds processed by the payment provider and reported to the Wix orders system. * When the value is false, the refund was reported to the Wix orders system but was not processed by the payment provider. ```json [ { "id": "caa2918c-6d38-08df-b484-09b702099e2b", "dateCreated": "2021-01-13T08:20:31.638Z", "amount": "26.4", "reason": "returned", "externalRefund": true, "paymentProviderTransactionId": "19615318-2134-4160-b886-cdde9c7ae88e" }, { "id": "7079923b-1c65-1c7a-37c9-3733de32ea1d", "dateCreated": "2021-01-13T08:26:09.419Z", "amount": "60.0", "externalRefund": false } ] ``` #### Subscription Information (subscriptionInfo) **Description**: Information about an order's subscription as a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: 'frequency' field - frequency of recurring payment. One of: * "DAY" - Daily * "WEEK" - Weekly * "MONTH" - Monthly * "YEAR" - Yearly ```json { "subscriptionInfo": { "id": "9275fb37-5377-434e-a484-6b6933896bc3", "cycleNumber": 3, "subscriptionSettings": { "frequency": "WEEK", "autoRenewal": true, "billingCycles": 1 }, "subscriptionOptionInfo": { "id": "0a7c4732-09c8-430c-b94e-f189cc0ce25a", "title": "Weekly", "description": "Weekly subscription" } } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "AbandonedCarts" Collection Fields ## Article: Wix Stores "AbandonedCarts" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-abandoned-carts-collection-fields.md ## Article Content: # Velo: Wix Stores "AbandonedCarts" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the AbandonedCarts collection in code, refer to it as `"Stores/AbandonedCarts"`. ```javascript wixData.query("Stores/AbandonedCarts") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your AbandonedCarts collection.
**Important:** You can query up to 100 items from the AbandonedCarts collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The AbandonedCarts collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Admin  * **Create:** None * **Update:** None * **Delete:** None You cannot change the AbandonedCarts collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ID of the abandoned cart. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Buyer Info (buyerInfo) **Description**: Buyer information as a JSON object. **Type**: Object **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note:** "identityType" is one of the following: * "MEMBER": Buyer is a logged-in site member. * "CONTACT": A contact has been created for the buyer. ```json { "id": "4kf9ka09-4e9f-a02d-972f-9a5844d9d9a2", "identityType": "CONTACT", "firstName": "John", "lastName":"Doe", "phone": "5555555555", "email": "john.doe@somedomain.com" } ``` ```javascript // filter a dataset by Buyer Info ID import wixData from 'wix-data'; $w("#myDataset").setFilter(wixData.filter() .eq("buyerInfo.id", ["5ceea2d0-a537-41dc-a51e-51fc8645afd9"])) ``` #### Date Abandoned (abandonTime) **Description**: Time the cart was abandoned. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Status (status) **Description**: Status of the abandoned cart. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes **Notes**: Either "ABANDONED" or "RECOVERED". #### Activities (activities) **Description**: List of activities for this order in an array of JSON objects. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Notes:** The "type" value is one of: * "UNRECOGNIZED\_TYPE" * "SCHEDULED" * "EMAIL\_SENT" * "EMAIL\_NOT\_SENT" * "NOTIFICATION\_SENT" * "TASK\_CREATED" * "CUSTOM\_ACTIVITY" ```json [ { "type": "SCHEDULED", "timestamp": "2019-01-28T15:43:28.986Z" }, { "type": "EMAIL_SENT", "timestamp": "2019-01-28T15:53:24.347Z" } ] ``` #### Total (total) **Description**: The order total. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores "Variants" Collection Fields ## Article: Wix Stores "Variants" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-variants-collection-fields.md ## Article Content: # Velo: Wix Stores "Variants" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Variants collection in code, refer to it as `"Stores/Variants"`. ```javascript wixData.query("Stores/Variants") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Variants collection. 
**Important:** You can query up to 100 items from the Variants collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The Products collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Variants collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The store variant ID (GUID) created by the server. It is a system field comprised of the product ID and the product variant ID, separated by a hyphen. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Variant ID (variantId) **Description**: The variant ID (GUID) that was created by the server. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Full Variant Name (fullVariantName) **Description**: The full name of the variant. **Type**: Text  **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Product ID (productId) **Description**: The product ID (GUID) that was created by the server. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Variant Name (variantName) **Description**: The name of the variant. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Product Name (productName) **Description**: The name of the product. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Managed Variant (managedVariant) **Description**: Indicates whether the variant is being managed for the product - enables unique SKU, price, and weight per variant. Also affects inventory data. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### SKU (sku) **Description**: Variant stock-keeping unit number. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Stock (stock) **Description**: Stock data for this variable in a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json "stock": { "trackQuantity": true, "quantity": 50, "inStock": true } ``` #### Choices (choices) **Description**: Displays the defined product options and choices as a JSON object. **Type**: JSON **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "Size": "Medium", "Color": "Black" } ``` #### Media (media) **Description**: Displays the main media item (image or video) for this product as it appears in the Store Manager. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Collections (collections) **Description**: The collections the variant belongs to. **Type**: Reference (Multiple Items) **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores Schema for Wix Search ## Article: Wix Stores Schema for Wix Search ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-e-commerce-stores/wix-stores-schema-for-wix-search.md ## Article Content: # Wix Stores Schema for Wix Search This article describes the Wix Store product fields you can [search](https://www.wix.com/velo/reference/wix-search.html#search), [sort](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#ascending), [filter](https://www.wix.com/velo/reference/wix-search.html#filter), and [apply facets](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#facets) to when using the [wix-search API](https://www.wix.com/velo/reference/wix-search.html). To search Wix Store products on your site, set the search document type as `"Stores/Products"`: ``` wixSearch.search(phrase) .documentType("Stores/Products") .find() .then( (results) => { // handle the results } ); ``` **Note** The Stores/Products fields supported by wix-search are not identical to the fields in your site's [Stores/Products collection](https://support.wix.com/en/article/velo-wix-stores-products-collection-fields). The following fields appear in each matching search result [document](https://www.wix.com/velo/reference/wix-search/wixsearchresult/documents) when searching Wix Store products: ## documentType **Description**: Document type that was searched. In this case, "Stores/Products". **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## \_id **Description**: Product ID. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## title **Description**: Product name. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## description **Description**: Product description. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No ## url **Description**: Relative URL of the product's page on your site. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## image **Description**: File source of the main media image for this product. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No ## sku **Description**: Product's stock keeping unit number. **Type**: String **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le ## inStock **Description**: Indicates whether the product is in stock. **Type**: Boolean **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: eq, ne ## options **Description**: Product options and choices. **Type**: Array of Strings **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: No **Can filter**: hasSome, hasAll The `options` array contains product option names (for example, `Color`) followed by the descriptions of the choices for that option (for example, `blue`, `black`). If there is more than one option for a product, the options and their choice descriptions are listed in sequence. The following is an example of an `options` array for a product with 2 options: `Color` and `Size`: ``` options: ["Color", "Orange", "Blue", "Size", "Small", "Medium", "Large"] ``` **Note** Faceting products with more than 1 product option may return unexpected results. ## collections **Description**: Collections the product belongs to. **Type**: Array of Strings **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: No **Can filter**: hasSome, hasAll ## onSale **Description**: Indicates whether the product is on sale. **Type**: Boolean **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: eq, ne --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Members "PrivateMembersData" Collection Fields ## Article: Wix Members "PrivateMembersData" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-members/wix-members-private-members-data-collection-fields.md ## Article Content: # Velo: Wix Members "PrivateMembersData" Collection Fields
**Warning:** This data collection is deprecated. Use the [Wix Members "FullData" collection](https://dev.wix.com/docs/velo/api-reference/wix-members-v2/full-data-collection-fields.md) instead.
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the PrivateMembersData collection in code, refer to it as `"Members/PrivateMembersData"`. ```javascript wixData.query("Members/PrivateMembersData") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your PrivateMembersData collection.  >**Note:** > You manage your Members in the [Site Members](https://support.wix.com/en/article/viewing-your-member-list) section of your dashboard. ### Permissions The PrivateMembersData collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Site member author * **Create:** None * **Update:** None * **Delete:** None You cannot change the PrivateMembersData collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### ID (\_id) **Description**: The member ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes  **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Login Email (loginEmail) **Description**: The email address the member uses to log in to your site. This is the address they supplied when they signed up. This is the Main field. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Name (name) **Description**: The member's full name. This information is concatenated from the "First Name" and "Last Name" fields. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### First Name (firstName) **Description**: The member's first name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Last Name (lastName) **Description**: The member's last name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Picture (picture) **Description**: Displays the image the member uploaded to their profile, if any. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Nickname (nickname) **Description**: Displays the name the member specified on their profile. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Slug (slug) **Description**: The member's URL-friendly name that is unique across your site. Typically this is made up of the member's email prefix. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Language (language) **Description**: Displays the member's locale based on their last login. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Status (status) **Description**: Indicates the member's current status: * Applicant: The member is waiting to be approved * Active: The member is approved and can access your site * Blocked: The member has been blocked and can no longer log in to your site **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Displays the date and time the member registered to the site. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, gt, lt **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Displays the last date and time the member's details were updated. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, gt, lt **Read-only**: Yes #### Last Login Date (lastLogin) **Description**: Displays the date and time when the member last logged in. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, gt, lt **Read-only**: Yes #### Emails (emails) **Description**: Displays all the member's email addresses in an array. **Type**: No **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json ["email1", "email2", ...] ``` #### Main Phone (mainPhone) **Description**: Displays the member's main phone number. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Phones (phones) **Description**: Displays all the member's phone numbers in an array. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json ["phone1", "phone2" ...] ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Members "FullData" Collection Fields ## Article: Wix Members "FullData" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-members/wix-members-full-data-collection-fields.md ## Article Content: # Velo: Wix Members "FullData" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the FullData collection in code, refer to it as `"Members/FullData"`. ```javascript wixData.query("Members/FullData") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your FullData collection. >**Note:** > You manage your Members in the [Site Members](https://support.wix.com/en/article/viewing-your-member-list) section of your dashboard.
**Important:** You can query up to 100 items from the Collections collection. Trying to query more than 100 items by raising the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) will result in an error.
### Permissions The FullData collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the FullData collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The member ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: eq, hasSome **Read-only**: Yes #### Login Email (loginEmail) **Description**: Email used by the member to log into the site. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: hasSome, eq **Read-only**: Yes #### First Name (firstName) **Description**: Member's first name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: hasSome, eq **Read-only**: Yes #### Last Name (lastName) **Description**: Member's last name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: hasSome, eq **Read-only**: Yes #### Phone (phone) **Description**: Member's phone number. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Email (email) **Description**: Member's email address. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Nickname (nickname) **Description**: The name that is displayed on the member's profile page, forum and or blog. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: hasSome, eq **Read-only**: Yes #### Slug (slug) **Description**: The member's URL-friendly name that is unique across your site. Typically this is made up of the member's email prefix. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Profile Photo (profilePhoto) **Description**: Displays the member's profile photo, if they uploaded one. **Type**: Image **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "url": "wix:image://v1/8b7eef_3deb121802514be7b698d0447559f9da~mv2.png/avatar-ga890ac591_1280.png", "height": 400, "width": 450, "offsetX": 0, "offsetY": 0, } ``` #### Cover Photo (coverPhoto) **Description**: Displays the member's cover photo on their profile page. **Type**: Image **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Satus (status) **Description**: Member site access status. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: Field value is one of:  * "PENDING" - Member created and waiting for approval by site owner. * "APPROVED" - Member approved and able to log in. * "OFFLINE" - Member is a guest author of the site and cannot log in. * "BLOCKED" - Member is blocked and cannot log into the site. * "UNKNOWN" - Insufficient permissions to get the status. #### Privacy Status (privacyStatus) **Description**: Member's privacy status. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: Field value is one of: * "PUBLIC" - The member profile is visible to everyone. * "PRIVATE" - The member profile is hidden from site visitors and members. The member is only returned to site contributors and apps with the appropriate permissions. #### Activity Status (activityStatus) **Description**: Member's activity status. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Note**: Field value is one of: * "ACTIVE" - The member can write forum posts and blog comments. * "MUTED" - The member cannot write forum posts or blog comments. #### Last Login Date (lastLoginDate) **Description**: Displays the last date and time the member last logged into the site. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Title (title) **Description**: Member's title. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Displays the date and time the member registered to the site. This is a system field and is hidden by default. **Type**: Date **Can** **connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Displays the last date and time the member's details were updated. This is a system field and is hidden by default. **Type**: Date **Can** **connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### About Plain Text (aboutPlain) **Description**: Displays the **About** information from the member's profile as plain text. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### About Rich Text (aboutRich) **Description**: Displays the **About** information from the member's profile as rich text. **Type**: Rich Content (object) **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Members "Badges" Collection Fields ## Article: Wix Members "Badges" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-members/wix-members-badges-collection-fields.md ## Article Content: # Velo: Wix Members "Badges" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Badges collection in code, refer to it as `"Members/Badges"`. ```javascript wixData.query("Members/Badges") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Badges collection.  ### Permissions The Badges collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Admin * **Create:** None * **Update:** None * **Delete:** None You cannot change the Badges collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: Badge ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Title (title) **Description**: Badge title. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Description (description) **Description**: Badge description. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Background Color (backgroundColor) **Description**: Background color of the badge. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Text Color (textColor) **Description**: Text color of the badge. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Icon (icon) **Description**: Badge icon. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Role ID (roleId) **Description**: ID of the badge's associated member [role](https://support.wix.com/en/article/creating-member-roles-6943237). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: The badge's unique URL as used in a dynamic page. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, gt, lt **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Date and time the badge was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Date and time the badge was last updated. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Members (members) **Description**: List of members the badge is assigned to. An array of member IDs. **Type**: Tags **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: hasSome **Read-only**: Yes ```javascript [ "23ab456c-de7f-890g-hi12-3j45kl6m7n8o", "3bc4567d-8e9f-0g12-3456-789012hijk34", "c45678d9-f0ef-1ghi-2345-678j9012kl3m" ] ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Members "PublicData" Collection Fields ## Article: Wix Members "PublicData" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/wix-members/wix-members-public-data-collection-fields.md ## Article Content: # Velo: Wix Members "PublicData" Collection Fields
**Warning:** This data collection is deprecated. Use the [Wix Members "FullData" collection](https://dev.wix.com/docs/velo/api-reference/wix-members-v2/full-data-collection-fields.md) instead.
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the PublicData collection in code, refer to it as `"Members/PublicData"`. ```javascript wixData.query("Members/PublicData") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your PublicData collection. >**Note:** > You manage your Members in the [Site Members](https://support.wix.com/en/article/viewing-your-member-list) section of your dashboard. ### Permissions The PublicData collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the PublicData collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### \[Custom Fields\] Custom fields from your Contacts collection can be added in your site's dashboard. You can define custom fields however you like. These fields are optional. They are **not** automatically added. Custom fields from your site's Contacts can be [added to your site Members in your site's dashboard](https://support.wix.com/en/article/customizing-your-member-profile-fields). #### ID (\_id) **Description**: The member ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: eq, hasSome, lt, gt, ne **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Displays the date and time the member registered to the site. **Type**: Date and Time **Can** **connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Displays the last date and time the member's details were updated. **Type**: Date and Time **Can** **connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Profile Photo (profilePhoto) **Description**: Displays the image the member uploaded to their profile, if any. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Cover Photo (coverPhoto) **Description**: Displays the cover / banner image the member uploaded to their profile, if any. **Type**: Image **Can connect to data**: Yes  **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Nickname (nickname) **Description**: Displays the name the member specified on their profile. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, hasSome **Read-only**: No #### Title (title) **Description**:  The member's title. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: The member's URL-friendly name that is unique across your site. Typically this is made up of the member's email prefix. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### About (Plain Text) (aboutPlain) **Description**: Displays the **About** information from the member's profile as plain text. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### About (Rich Text) (aboutRich) **Description**: Displays the **About** information from the member's profile as rich text. **Type**: Rich content **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Blog "Categories" Collection Fields ## Article: Wix Blog "Categories" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-categories-collection-fields.md ## Article Content: # Velo: Wix Blog "Categories" Collection Fields >**Note:** > Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md). This document describes the permissions and fields in your Categories collection.  ### Permissions The Categories collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Categories collection permissions.  ### Fields The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ID of the blog category. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Label (label) **Description**: The name of the category as it appears in the Blog menu.  **Type**: Text  **Can connect to data**: Yes  **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, contains, startsWith, hasSome  **Read-only**: Yes #### Post Count (postCount) **Description**: The number of posts in the category. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Posts (posts) **Description**: A list of posts in this category, referenced from the [Blog/Posts](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-posts-collection-fields.md) collection. **Type**: Multiple-item reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Language (language) **Description**: The category language. **Type**: Language **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Translation ID (translationId) **Description**: ID of the category's translations.  **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Category Page Slug (categoryPageSlug) **Description**: The slug of the category page on your published site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Category Page URL (categoryPageUrl) **Description**: The URL of the category page on your published site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Cover Image (coverImage) **Description**: Category cover image. **Type**: Image **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Description (description) **Description**: Category description. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: Slug of the category to retrieve. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Blog "Posts" Collection Fields ## Article: Wix Blog "Posts" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-posts-collection-fields.md ## Article Content: # Velo: Wix Blog "Posts" Collection Fields >**Note:** > Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md). To use the Posts collection in code, refer to it as `"Blog/Posts"`. wixData.query("Blog/Posts") .find() .then( (results) => { // handle the results } ); This document describes the permissions and fields in your Posts collection.  ### Permissions The Posts collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Posts collection permissions.  ### Fields The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ID of the post. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Title (title) **Description**: The title of the post. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, contains, startsWith, hasSome **Read-only**: Yes #### Published Date (publishedDate) **Description**: The date the post was originally published. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Cover Image (coverImage) **Description**: The cover image of the post. **Type**: Image **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Categories (categories) **Description**: The categories assigned to the post, referenced from the [Blog/Categories](https://support.wix.com/en/article/velo-wix-blog-categories-collection-fields) collection. **Type**: Multiple-item reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: hasAll, hasSome, contains **Read-only**: Yes #### Main Category (mainCategory) **Description**: **Type**: Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Plain Content (plainContent) **Description**: The text of the post without any formatting. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Rich Content (richContent) **Description**: **Type**: Rich Content **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Excerpt (excerpt) **Description**: A few lines of text that appear in the blog feed. Defined in Post Settings or default of first 160 characters of the post. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Hashtags (hashtags) **Description**: List of all hashtags in the post. **Type**: Tags **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: hasAll, hasSome **Read-only**: Yes #### View Count (viewCount) **Description**: The number of times the post was viewed. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes >**Note** > View Count data used on your site pages may require up to 24 hours to update. To make sure new post views are included on your site in real time, [disable caching](https://support.wix.com/en/article/caching-your-sites-pages) for any page that uses View Count data. Note that disabling caching may increase your page loading time. #### Like Count (likeCount) **Description**: The number of likes the post received (from members only). **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes >**Note** > Like Count data used on your site pages may require up to 24 hours to update. To make sure new post likes are included on your site in real time, [disable caching](https://support.wix.com/en/article/caching-your-sites-pages) for any page that uses Like Count data. Note that disabling caching may increase your page loading time. #### Comment Count (commentCount) **Description**: The number of comments the post received (from members only). **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Featured (featured) **Description**: Indicates whether the post is set as featured in the post settings. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Pinned (pinned) **Description**: Indicates whether the post was pinned to the top of the page in the live site. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Last Published Date (lastPublishedDate) **Description**: The most recent publish date of the post. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Cover Image Displayed (coverImageDisplayed) **Description**: Indicates whether the cover image is displayed in the feed. If false, the cover image is displayed only when you share the post on social media. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Post Page URL (postPageURL) **Description**: The relative URL of the post page on your published site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Language (language) **Description**: **Type**: Language **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Translation ID (translationId) **Description**: **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Time to Read (timeToRead) **Description**: Estimated time in minutes required to read the post. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Related Posts (relatedPosts) **Description**: **Type**: Multi-Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Tags (tags) **Description**: **Type**: Multi-Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Author (author) **Description**: **Type**: Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Slug (slug) **Description**: **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### PaidPlans (paidPlans) **Description**: **Type**: Multi-Reference **Can connect to data**: **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Blog "Tags" Collection Fields ## Article: Wix Blog "Tags" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-tags-collection-fields.md ## Article Content: # Velo: Wix Blog "Tags" Collection Fields >**Note:** > Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md). To use the Tags collection in code, refer to it as "Blog/Tags". ```javascript wixData.query("Blog/Tags") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Tags collection.  ### Permissions The Tags collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Tags collection permissions.  ### Fields The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ID of the blog tag. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Label (label) **Description**: The name of the tag as it appears in the Blog menu.  **Type**: Text  **Can connect to data**: Yes  **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, contains, startsWith, hasSome  **Read-only**: Yes #### Post Count (postCount) **Description**: The number of posts with the tag. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Posts (posts) **Description**: A list of posts with this tag, referenced from the [Blog/Posts](https://support.wix.com/en/article/velo-wix-blog-posts-collection-fields) collection. **Type**: Multiple-item reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Tag Page URL (tagPageUrl) **Description**: The URL of the tag page on your published site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: Slug of the tag to retrieve. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Blog Schema for Wix Search ## Article: Wix Blog Schema for Wix Search ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-schema-for-wix-search.md ## Article Content: # Wix Blog Schema for Wix Search This article describes the Wix Blog fields you can [search](https://www.wix.com/velo/reference/wix-search.html#search), [sort](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#ascending), [filter](https://www.wix.com/velo/reference/wix-search.html#filter), and [apply facets](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#facets) to when using the [wix-search API](https://www.wix.com/velo/reference/wix-search.html). To search Wix Blog posts on your site, set the search document type as `”Blog/Posts"`: ```javascript wixSearch.search(phrase) .documentType("Blog/Posts") .find() .then( (results) => { // handle the results } ); ``` >**Note:** > The Blog fields supported by wix-search are not identical to the fields in your site's [Blog/Posts collection](https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-blog/wix-blog-posts-collection-fields.md). The following fields appear in each matching search result [document](https://www.wix.com/velo/reference/wix-search.WixSearchResult.html#documents) when searching Wix Blog services: #### documentType **Description**: Document type that was searched. In this case, “Blog/Posts”. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### \_id **Description**: The ID of the post. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### title **Description**: The title of the post. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No #### description **Description**: Text of the post. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No #### url **Description**: Relative URL of the post page on your site. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### image **Description**: File source of the post’s cover image. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### hashTags **Description**: List of hashtags in the post. **Type**: Array of Strings **Can search the content of this field**: No **Can facet**: Yes **Can sort**: No **Can filter**: hasSome, hasAll #### tags **Description**: List of tags in the post. **Type**: Array of Strings **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: Yes **Can filter**: hasSome, hasAll #### tagIds **Description**: List of tag IDs in the post. **Type**: Array of Strings **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: hasSome, hasAll #### publishDate **Description**: Date and time the post was originally published. **Type**: Date **Can search the content of this field**: No **Can facet**: No **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Events "Events" Collection Fields ## Article: Wix Events "Events" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-events/wix-events-events-collection-fields.md ## Article Content: # Velo: Wix Events "Events" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Events collection in code, refer to it as `"Events/Events"`. ```javascript wixData.query("Events/Events") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Events collection. ### Permissions The Events collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Events collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The event ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Title (title) **Description**: The event's name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: lt, lte, gt, gte, hasSome, urlized **Read-only**: Yes #### Slug (slug) **Description**: The event's slug, which is its URL-friendly name that is unique across your site. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, urlized **Read-only**: Yes #### Description (description) **Description**: The short teaser specified in the Events Dashboard. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### About (about)
**Deprecated** This field is deprecated and replaced by `longDescription`.
**Description**: The information you entered in the "About Your Event" section of the Events Dashboard. **Type**: Rich Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Long Description (longDescription) **Description**: The information you entered in the "About Your Event" section of the Events Dashboard. **Type**: Rich Content **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Main Image (mainImage) **Description**: Displays the image for this event. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Start (start) **Description**: The start date and time of the event, if any. If the event schedule is TBD, this field is blank. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### End (end) **Description**: The end date and time of the event, if any. If the event schedule is TBD, this field is blank. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Time Zone (timeZoneId) **Description**: The time zone for your event. If the event schedule is TBD, this field is blank. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Schedule TBD (scheduleTbd) **Description**: Indicates whether the time of this event is marked as TBD. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Schedule Formatted (scheduleFormatted) **Description**: If the event has a date and time, the event's date and time, formatted according to your local settings, are displayed. Otherwise, the TBD message from the Events Dashboard is displayed. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Start date formatted (scheduleStartDateFormatted) **Description**: The event's start date, formatted to your local settings, are displayed. If the event schedule is TBD, this field is blank. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Start time formatted (scheduleStartTimeFormatted) **Description**: The event's start time, formatted to your local settings, are displayed. If the event schedule is TBD, this field is blank. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Location Name (locationName) **Description**: If the event has a location, that location is displayed. Otherwise, the TBD message from the Events Dashboard is displayed. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Location latitude (latitude) **Description**: Event location address's latitude. **Type**:Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Location longitude (longitude) **Description**: Event location address's longitude. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Location Address (locationAddress) **Description**: The full address of the location. If the location is TBD, this field is blank. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Created (created) **Description**: Displays the date and time the event was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Modified (modified) **Description**: Displays the date and time the event was last modified. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Status (status) **Description**: The current status of the event, as follows: * SCHEDULED: The event date is in the future. * STARTED: The event start time has arrived. * ENDED: The event end time has passed. * CANCELED: The event was canceled. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Type (type) **Description**: The type of event, as follows: * RSVP: The event is set up to accept RSVP registration via your Wix page. * TICKETS: The event is set up to sell tickets. * EXTERNAL: The event is set up to accept registration via an external page **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Registration Status (registrationStatus) **Description**: The registration status, as follows: * OPEN\_RSVP: RSVP registration is open. * OPEN\_TICKETS: Ticket sales for this event are open. * OPEN\_EXTERNAL: RSVP registration or ticket sales are closed but registration via an external site is open. * OPEN-RSVP\_WAITLIST: The RSVP list is full but requests can be added to a waitlist. * CLOSED: RSVP registration or ticket sales are closed. * CLOSED\_MANUALLY: RSVP registration or ticket sales have been closed manually in the Events Dashboard. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Lowest Price Formatted (lowestPriceFormatted) **Description**: The lowest price available, formatted with your currency. * If this event has RSVP registration or is external, this field is blank.  * If tickets are sold for this event, the lowest price available is displayed.  * If free tickets are offered, "Free" is displayed. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Highest Price Formatted (highestPriceFormatted) **Description**: The highest price available, formatted with your currency. * If this event has RSVP registration, this field is blank.  * If tickets are sold for this event, the highest price available is displayed.  * If only free tickets are offered, "Free" is displayed. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Google Calendar URL (googleCalendarUrl) **Description**: Displays an "Add to Google calendar" link to the event. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### iCalendar URL (iCalendarUrl) **Description**: Displays an iCalendar download link to the event. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Site Event Page URL (siteEventPageUrl) **Description**: The event's URL-friendly name that is unique across your site. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Registration URL (registrationURL) **Description**: Registration URL if registration for the Wix event managed externally. Guests go to this URL to register. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Creator ID (UserID) **Description**: ID of the creator of the event.  **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Tickets "Tickets" Collection Fields ## Article: Wix Tickets "Tickets" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-events/wix-tickets-tickets-collection-fields.md ## Article Content: # Velo: Wix Tickets "Tickets" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Events collection in code, refer to it as `"Events/Tickets"`. ```javascript wixData.query("Events/Tickets") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Tickets collection. ### Permissions The Events collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Tickets collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ticket ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: is, is not **Read-only**: Yes #### Name (name) **Description**: The ticket's name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: is, is not **Read-only**: Yes #### Event (event) **Description**: The event that the ticket is valid for. **Type**: Reference to the Events/Events collection **Can connect to data**: Yes, to the referenced item's fields **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Description (description) **Description**: Description of the ticket which appears on the purchase page to give customers more details about what’s included in the ticket. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price (price) **Description**: The ticket price. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Currency (currency) **Description**: The currency of the ticket price. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Tax Amount (tax) **Description**: The tax charged for the ticket. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Fee Amount (fee) **Description**: The fee for the ticket. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Limit Per Checkout (limitPerCheckout) **Description**: Maximum number of tickets that can be checked out at once. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Remaining Tickets (remainingTickets) **Description**: The number of remaining tickets. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Notes**: The maximum value in this field is 20. If there are more than 20 tickets remaining, the value in this field will be 20. #### Policy (policy) **Description**: The ticket policy which appears on the bottom of the ticket. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Final Price (finalPrice) **Description**: The final price for the ticket. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Forum "Categories" Collection Fields ## Article: Wix Forum "Categories" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-forum/wix-forum-categories-collection-fields.md ## Article Content: # Velo: Wix Forum "Categories" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
This document describes the permissions and fields in your Categories collection.  ### Permissions The Categories collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the Categories collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The category ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Parent ID (parentId) **Description**: ID of the parent category (for subcategories). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Name (name) **Description**: Category name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: eq, ne, contains, startsWith, hasSome **Read-only**: Yes #### Header Title (headerTitle) **Description**: Title in the category header. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: eq, ne, contains, startsWith, hasSome **Read-only**: Yes #### Description (description) **Description**: Description in the category header. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: contains, startsWith, hasAll **Read-only**: Yes #### Header Type (headerType) **Description**: Type of category header. **Type**: String **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes **Notes**: Either "COLOR" or "IMAGE". #### Header Image (headerImage) **Description**: Header Image. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Header Image Overlay Color (headerImageOverlayColor) **Description**: Header image overlay color. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Header Background Color (headerBackgroundColor) **Description**: Header background color. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Header Text Color (headerTextColor) **Description**: Header text color. **Type**: No **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Rank (rank) **Description**: Category order, where 0 indicates the first category. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Post Count (postCount) **Description**: Number of posts in the category. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Post View Count (postViewCount) **Description**: Number of times the posts in the category have been viewed. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Write Protection **Description**:  **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No  **Can be filtered**: No  **Read-only**: Yes #### Page URL (pageUrl) **Description**: URL of the category page. **Type**: URL **Can connect to data**: Yes, can connect as a button click action. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: The category's slug, which is its URL-friendly name that is unique across the forum. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Forum "Comments" Collection Fields ## Article: Wix Forum "Comments" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-forum/wix-forum-comments-collection-fields.md ## Article Content: # Velo: Wix Forum "Comments" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
This document describes the permissions and fields in your Comments collection.  ### Permissions The Comments collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the Comments collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The comment ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, contains **Read-only**: Yes #### Parent ID (parentId) **Description**: ID of the parent comment (for replies). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Post ID (postId) **Description**: ID of the post that the comment is commenting on. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Owner ID (ownerId) **Description**: ID of the comment owner. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Plain Content (plainContent) **Description**: Plain content of the comment. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Reply Count (replyCount) **Description**: Number of replies to the comment. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Like Count (likeCount) **Description**: Number of likes on the comment. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Date and time the comment was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Edited Date (\_editedDate) **Description**: Date and time the comment was last edited. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Last Activity Date (lastActivityDate) **Description**: Date and time of the last activity on the comment. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Page URL (pageUrl) **Description**: URL of the comment page. **Type**: URL **Can connect to data**: Yes, can connect as a button click action. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Upvote Count (upvoteCount) **Description**: Number of upvotes on the comment. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Downvote Count (downvoteCount) **Description**: Number of downvotes on the comment. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Score (score) **Description**: Number of upvotes minus the number of downvotes. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Forum "Posts" Collection Fields ## Article: Wix Forum "Posts" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-forum/wix-forum-posts-collection-fields.md ## Article Content: # Velo: Wix Forum "Posts" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
This document describes the permissions and fields in your Posts collection.  ### Permissions The Posts collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the Posts collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The post ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes #### Category ID (categoryId) **Description**: Post category ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Owner ID (ownerId) **Description**: Post owner's ID. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Title (title) **Description**: Post title. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: eq, ne, contains, startsWith, hasSome **Read-only**: Yes #### Plain Content (plainContent) **Description**: Plain content of the forum post. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: contains, startsWith, hasAll **Read-only**: Yes #### Best Answer Comment ID (bestAnswerCommentId) **Description**: ID of the comment marked as the best answer. **Type**: String **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Pinned (pinned) **Description**: Whether the post is pinned. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Commenting Disabled (commentingDisabled) **Description**: Whether comments are disabled for the post. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Comment Count (commentCount) **Description**: Number of comments on the post. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Like Count (likeCount) **Description**: Number of likes on the post. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### View Count (viewCount) **Description**: Number of times the post has been viewed. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Date and time the post was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Edited Date (\_editedDate) **Description**: Date and time the post was last edited. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Last Activity Date (lastActivityDate) **Description**: Date and time of the last activity performed on the post. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte **Read-only**: Yes #### Page URL (pageUrl) **Description**: URL of the post page. **Type**: URL **Can connect to data**: Yes, can connect as a button click action. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Slug (slug) **Description**: The post's slug, which is its URL-friendly name that is unique across the forum. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Post Type (postType) **Description**: Type of post. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes **Notes**: Either "DISCUSSION" or "QUESTION" --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Forum Schema for Wix Search ## Article: Wix Forum Schema for Wix Search ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-forum/wix-forum-schema-for-wix-search.md ## Article Content: # Wix Forum Schema for Wix Search This article describes the Wix Forum product fields you can [search](https://www.wix.com/velo/reference/wix-search.html#search), [sort](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#ascending), [filter](https://www.wix.com/velo/reference/wix-search.html#filter), and [apply facets](https://www.wix.com/velo/reference/wix-search.WixSearchBuilder.html#facets) to when using the [wix-search API](https://www.wix.com/velo/reference/wix-search.html). To search Wix Forum posts and comments on your site, set the search document type as `"Forum/Content"`: ```javascript wixSearch.search(phrase) .documentType("Forum/Content") .find() .then( (results) => { // handle the results } ); ``` >**Note** > The Forum fields supported by wix-search are not identical to the fields in your site's [Forum/Posts](https://support.wix.com/en/article/velo-wix-forum-posts-collection-fields) and [Forum/Comments](https://support.wix.com/en/article/velo-wix-forum-comments-collection-fields) collections. The following fields appear in each matching search result [document](https://www.wix.com/velo/reference/wix-search/wixsearchresult/documents) when searching Wix Forum content: #### documentType **Description**: Document type that was searched. In this case, "Forum/Content". **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### \_id **Description**: Post or comment ID. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### title **Description**: Post title. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No #### description **Description**: Text of the post or comment. **Type**: String **Can search the content of this field**: Yes **Can facet**: No **Can sort**: No **Can filter**: No #### url **Description**: Relative URL of the post page on your site. For comments, the URL points to the location of the comment on your post page. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### image **Description**: File source of the post image. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### contentType **Description**: Indicates whether the document is a post or a comment. Value can be "POST" or "COMMENT". **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: eq, ne You can use this field to filter search results so that only Forum posts or comments are returned. For example, the following code example demonstrates how to search only for Forum comments and not posts:  ```javascript wixSearch.search(phrase) .documentType("Forum/Posts") .eq("contentType", "COMMENT") .find() .then( (results) => { // handle the results } ); ``` #### postId **Description**: ID of the post the comment is associated with. Only relevant for comments. **Type**: String  **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le  #### ownerId **Description**: Unique ID of the site visitor who posted the post or comment. **Type**: String  **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le  #### categoryId **Description**: ID of the category associated with the post. **Type**: String **Can search the content of this field**: No **Can facet**: Yes **Can sort**: No **Can filter**: in, eq, ne, gt, ge, lt, le  #### hashTags **Description**: Hashtags in the post. **Type**: Array of Strings **Can search the content of this field**: No **Can facet**: Yes **Can sort**: No **Can filter**: hasSome, hasAll #### lastActivityDate **Description**: Date and time of the last action performed on the post or comment. **Type**: Date **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le  #### likeCount **Description**: Number of likes the post or comment received. **Type**: Number **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### viewCount **Description**: Number of views the post received. **Type**: Number **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### totalComments **Description**: Number of comments the post received. **Type**: Number **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### postTitle **Description**: Title of the post. Only relevant for posts. **Type**: String **Can search the content of this field**: Yes **Can facet**: Yes **Can sort**: No **Can filter**: No #### categoryTitle **Description**: Title of the category. **Type**: String **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### parentId **Description**: ID of the parent comment. **Type**: String **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### createdDate **Description**: Date and time the comment or post was created. **Type**: Date **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: eq, ne, gt, ge, lt, le #### upvoteCount **Description**: Number of upvotes on the comment. **Type**: Number **Can search the content of this field**: No **Can facet**: Yes **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### downvoteCount **Description**: Number of downvotes on the comment. **Type**: Number **Can search the content of this field**: No **Can facet**: No **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### score **Description**: Number of upvotes minus the number of downvotes. **Type**: Number **Can search the content of this field**: No **Can facet**: No **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### editedDate **Description**: Date and time the comment or post was last edited. **Type**: Date **Can search the content of this field**: No **Can facet**: No **Can sort**: Yes **Can filter**: eq, ne, gt, ge, lt, le #### mentions **Description**: Users mentioned in the post. **Type**: Array of strings **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### markedComments **Description**: IDs of the marked comments for the post (selected by forum admin or site owner). **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### marked **Description**: Whether the comment is marked. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: Yes **Can filter**: in, eq, ne, gt, ge, lt, le #### commentInteraction **Description**: Defines what interaction may be applied on the comment. **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### images **Description**: Images from the post content. **Type**: Nested **Can search the content of this field**: No **Can filter**: in, eq, ne, gt, ge, lt, le **Nested fields**: #### images.id **Description**: WixMedia image ID (nested under `images`). **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### images.url **Description**: URL of the image (nested under `images`). **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### images.height **Description**: Original height of the image (nested under `images`). **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No #### images.width **Description**: Original width of the image (nested under `images`). **Type**: String **Can search the content of this field**: No **Can facet**: No **Can sort**: No **Can filter**: No --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Marketing "Coupons" Collection Fields ## Article: Wix Marketing "Coupons" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-marketing/wix-marketing-coupons-collection-fields.md ## Article Content: # Velo: Wix Marketing "Coupons" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Coupons collection in code, refer to it as `"Marketing/Coupons"`. ```javascript wixData.query("Marketing/Coupons") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Coupons collection. ### Permissions The Coupons collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Admin  * **Create:** None * **Update:** None * **Delete:** None You cannot change the Coupons collection's permissions.  ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The ID of the coupon. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Name (name) **Description**: Name of the coupon. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Code (code) **Description**: Coupon code. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, contains, startsWith **Read-only**: Yes #### Start Time (startTime) **Description**: Start date and time of the coupon. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Expiration Time (expirationTime) **Description**: End date and time of the coupon. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Usage Limit (usageLimit) **Description**: Maximum number of times a coupon can be used. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Limit Per Customer (limitPerCustomer) **Description**: Maximum number of times the coupon can be used **per customer**. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Applies to Subscriptions (appliesToSubscriptions) **Description**:  **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**:  **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes #### Limited To One Item (limitedToOneItem) **Description**: Indicates whether the coupon is limited to 1 discount per order. If true and a customer buys multiple items that the coupon applies to, only the lowest priced item is discounted. **Type**: Boolean **Can connect to data**: **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes **Note:** This field only applies to coupons with Type "moneyOffAmount" or "percentOffRate". #### Active (active) **Description**: Indicates whether the coupon is currently [active](https://support.wix.com/en/article/wix-stores-deleting-a-coupon). **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Minimum Subtotal (minimumSubtotal) **Description**: The coupon can be used when the order subtotal is over this amount. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Scope (scope) **Description**: Scope for the coupon as defined in a JSON object. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "namespace": "string", "group": { "name": "string", "entityId": "string" } } ``` The following table lists the available options for Namespace and Group: |Namespace |Group | |---|---| | stores | product | | | collection | | bookings | service | | events | event | | | ticket | You can define the scope for a coupon as an entire namespace, a group within a namespace, or an item within a group. See the examples below. **Example:** Scope for the coupon is defined as all products sold in your store. ```json { "namespace": "stores" } ``` **Example:** Scope for the coupon is defined as a specific product sold in your store. ```json { "namespace": "stores", "group": { "name":"product", "entityId": "82921602-92d2-1787-08c8-207d12ef41d2" // ID of product } } ``` Since the **events** namespace has both **ticket** and **event** groups, and you can't apply a single coupon to all tickets and events at the same time, you can't define the scope as just the namespace **events**. For events you need to define the scope as a group or a specific item in a group. **Example:** Scope for the coupon is defined as all tickets for events. ```json { "namespace": "events", "group": { "name":"ticket" } } ``` **Filtering with Scope** You can filter using one of 3 scopes: * "scope.namespace" * "scope.group.name" * "scope.group.entityId" **Filtering Example:** Query the Coupons collection for coupons within the Stores namespace. ```javascript wixData.query("Marketing/Coupons").eq("scope.namespace", "stores") ``` **Filtering Example:** Query the Coupons collection for coupons applied to a specific product. ```javascript wixData.query("Marketing/Coupons").eq("scope.group.entityId", "82921602-92d2-1787-08c8-207d12ef41d2") ``` #### Type (type) **Description**: The type of coupon (see note for available options). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-only**: Yes **Note:** The "type" value is one of the following: * "BuyXGetY" - Free products when making a purchase * "FixedPrice" - Specific sale price * "FreeShipping" - Free shipping * "MoneyOff" - Fixed price discount * "PercentOff" - Discount as a percentage #### Buy X Get Y (buyXGetY) **Description**: A JSON object representing X and Y in the following scenario: if a visitor purchases X number of products, they receive Y number of products for free. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json { "x": 3, "y": 1 } ``` #### Fixed Price Amount (fixedPriceAmount) **Description**: A specific sale price. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Free Shipping (freeShipping) **Description**: Indicates whether shipping is free. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Money Off Amount (moneyOffAmount) **Description**: A fixed amount subtracted from the original price. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Percent Off Rate (percentOffRate) **Description**: A percentage subtracted from the original price. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Date Created (\_dateCreated) **Description**: The date the coupon was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Number of Usages (numberOfUsages) **Description**: The total number of times a coupon was used by all customers. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, lte, gt, gte **Read-only**: Yes #### Expired (expired) **Description**: Indicates whether the expiration time passed and the coupon is expired. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne **Read-only**: Yes #### Display Data (displayData) **Description**: Display information for the item the coupon is applicable for defined as a JSON object (e.g. a Stores product or a Bookings service). **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes **Note:** This field is only relevant when the coupon is applied to a specific item. When the coupon is applied to all items in a group (e.g. all products), Display Data is empty.  ```json "displayData": { "formattedPrice": "string", "mediaItem": "string", "name": "string" } ``` #### App ID (appId) **Description**: The ID of the 3rd-party app that created the coupon. Empty if created by the site owner. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes ### Related APIs Use the following APIs to work with data from the Coupons collection: * [wix-marketing-backend.coupons](https://www.wix.com/corvid/reference/wix-marketing-backend.coupons.html) * [wix-data](https://www.wix.com/corvid/reference/wix-data.html) * [wix-dataset](https://www.wix.com/corvid/reference/wix-dataset.html) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Media "Files" Collection Fields ## Article: Wix Media "Files" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-media/wix-media-files-collection-fields.md ## Article Content: # Velo: Wix Media "Files" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
When you add a file from your Media Manager to your site, the 'Files' collection is automatically added to your site. To use the Files collection in code, refer to it as `"Media/Files"`. ```javascript wixData.query("Media/Files") .find() .then( (results) => { // handle the results } ); ``` >**Note:** > When using this collection, you can't set up the site to [link to the next or previous dynamic page](https://support.wix.com/en/article/linking-to-a-dynamic-page#linking-to-the-next-or-previous-dynamic-page). This document describes the permissions and fields in your Files collection.  ### Permissions The Files collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the Files collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### ID (\_id) **Description**: Unique File ID. This is a system field and is hidden by default. This is the [Main field](https://support.wix.com/en/article/about-your-database-collection-fields#main-fields). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Media Type (mediaType) **Description**: The file's media type, such as image and document. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Is Private (isPrivate) **Description**: The file's privacy setting. **Type**: Boolean **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Size in Bytes (sizeInBytes) **Description**: The file's size in bytes. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Mime Type (mimeType) **Description**: The file's mime type such as 'image/png', or 'audio/webm'. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### File URL (fileUrl) **Description**: The file's Wix media URL. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes Example URL: `wix:image://v1/8b7eef_3deb121802514be7b698d0447559f9da~mv2.png/avatar-ga890ac591_1280.png#originWidth=1280&originHeight=1280` #### Original File Name (originalFileName) **Description**: The file's original file name. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Hash (hash) **Description**: The file's checksum/hash. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Labels (labels) **Description**: The file's labels assigned to the file by the Media Manager. **Type**: Array of Strings **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Parent Folder ID (parentFolderId) **Description**: The file's parent folder ID. **Type**: String **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Parent Folder (parentFolder) **Description**: Reference to the file's parent folder item, as listed in the Folders collection. **Type**: Reference **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Icon URL (iconUrl) **Description**: The file icon's URL. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Icon Image (iconImage) **Description**: The file's icon as an image. **Type**: Image **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Source URL (sourceUrl) **Description**: The file's source URL, such as an external link where the file was imported from. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Width (width) **Description**: The file's width in pixels. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Height (height) **Description**: The file's height in pixels. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Op Status (opStatus) **Description**: Status of the file being uploaded, for example, 'READY' or 'IN-QUEUE'. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Date and time the file was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Date and time the file was most recently updated (created or renamed). This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Media "Folders" Collection Fields ## Article: Wix Media "Folders" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-media/wix-media-folders-collection-fields.md ## Article Content: # Velo: Wix Media "Folders" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
When you add a file from your Media Manager to your site, the 'Folders' collection is automatically added to your site. To use the Folders collection in code, refer to it as `"Media/Folders"`. ```javascript wixData.query("Media/Folders") .find() .then( (results) => { // handle the results } ); ``` >**Note:** > When using this collection, you can't set up the site to [link to the next or previous dynamic page](https://support.wix.com/en/article/linking-to-a-dynamic-page#linking-to-the-next-or-previous-dynamic-page). This document describes the permissions and fields in your Folders collection.  ### Permissions The Folders collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the Folders collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### ID (\_id) **Description**: Unique ID of the folder. Same as Folder ID. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Folder ID (folderId) **Description**: Unique Folder ID. Same as ID. This is the [Main field](https://support.wix.com/en/article/about-your-database-collection-fields#main-fields). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Folder Name (folderName) **Description**: Unique name of the folder. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes #### Parent Folder ID (parentFolderId) **Description**: The folder's parent folder ID. **Type**: String **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-only**: Yes #### Created Date (\_createdDate) **Description**: Date and time the folder was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Updated Date (\_updatedDate) **Description**: Date and time the folder was most recently updated (created or renamed). This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: No **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Media "FileshareFiles" Collection Fields ## Article: Wix Media "FileshareFiles" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-media/wix-media-fileshare-files-collection-fields.md ## Article Content: # Velo: Wix Media "FileshareFiles" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
When you add the [Wix File Share application](https://support.wix.com/en/article/about-the-file-share-app) to your site, the FileshareFiles collection is automatically added to your site. To use the FileshareFiles collection in code, refer to it as `"Media/FileshareFiles"`. ```javascript wixData.query("Media/FileshareFiles") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your FileshareFiles collection.  ### Permissions The FileshareFiles collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the FileshareFiles collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### Name (name) **Description**: Name of file. This is the [Main field](https://support.wix.com/en/article/about-your-database-collection-fields#main-fields). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Owner (\_owner) **Description**: Unique identifier of the user who created the file. Can be a site owner, [contributor](https://support.wix.com/en/article/about-roles-permissions-contributors), or member. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, hasAll, contains **Read-only**: Yes #### Date Created (\_createdDate) **Description**: The date and time the file was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Parent Folder (parentFolder) **Description**: Reference to the file's parent folder, as listed in the [FileshareFolders](https://support.wix.com/en/article/velo-wix-media-filesharefolders-collection-fields) collection. **Type**: Reference **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Path (path) **Description**: Array of objects representing the full path from root to the parent folder of the file. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json [ { "id": "f3788ddf-8615-4ea9-8baf-db57252f89d7", "name": "/" }, { "id": "7b1a6e48-56fd-4e33-ba2e-39fe1227190f", "name": "pictures" } ] ``` #### Favorite Count (favoriteCount) **Description**: The number of times the file was marked as a favorite. Both site members and visitors can mark a file as a favorite. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Favorite (favorite) **Description**: Indicates whether the file was marked as a favorite by the current visitor. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### View Count (viewCount) **Description**: The number of times unique visitors viewed the file. Note that multiple views by the same visitor and downloading the file do not increment the view count. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Viewed (viewed) **Description**: Indicates whether the current visitor viewed the file. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Extension (extension) **Description**: File extension. Examples: pdf, png. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Size (size) **Description**: Size of the file in bytes. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### \_id (\_id) **Description**: File ID. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, hasAll, contains **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Media "FileshareFolders" Collection Fields ## Article: Wix Media "FileshareFolders" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-media/wix-media-fileshare-folders-collection-fields.md ## Article Content: # Velo: Wix Media "FileshareFolders" Collection Fields
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
When you add the [Wix File Share application](https://support.wix.com/en/article/about-the-file-share-app) to your site, the FileshareFolders collection is automatically added to your site. To use the FileshareFolders collection in code, refer to it as `"Media/FileshareFolders"`. ```javascript wixData.query("Media/FileshareFolders") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your FileshareFolders collection.  ### Permissions The FileshareFolders collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** None * **Update:** None * **Delete:** None You cannot change the FileshareFolders collection permissions. ### Fields >**Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site’s [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default.  #### Name (name) **Description**: Name of folder. This is the [Main field](https://support.wix.com/en/article/about-your-database-collection-fields#main-fields). **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Owner (\_owner) **Description**: Unique identifier of the user who created the folder. Can be a site owner, [contributor](https://support.wix.com/en/article/about-roles-permissions-contributors), or member. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, hasAll, contains **Read-only**: Yes #### Date Created (\_createdDate) **Description**: The date and time the folder was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Parent Folder (parentFolder) **Description**: Reference to the folder's parent folder, as listed in this collection. **Type**: Reference **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: Yes **Read-only**: Yes #### Path (path) **Description**: Array of objects representing the full path from root to folder. **Type**: JSON **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes ```json [ { "id": "f3788ddf-8615-4ea9-8baf-db57252f89d7", "name": "/" }, { "id": "7b1a6e48-56fd-4e33-ba2e-39fe1227190f", "name": "pictures" } ] ``` #### Favorite Count (favoriteCount) **Description**: The number of times the folder was marked as a favorite. Both site members and visitors can mark a folder as a favorite. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Favorite (favorite) **Description**: Indicates whether the folder was marked as a favorite by the current visitor. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### View Count (viewCount) **Description**: The number of times unique visitors viewed (drilled down to) the folder. Note that multiple views by the same visitor and downloading the folder do not increment the view count. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Viewed (viewed) **Description**: Indicates whether the current visitor viewed (drilled down to) the folder. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Children Count (childrenCount) **Description**: The number of files and folders directly inside this folder. Note that subfolder children are not included in the count. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### Last Modified (lastModified) **Description**: Date and time the folder was most recently changed (created or renamed). Note that subfolder activity does not affect this field. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, lt, lte, gt, gte, hasSome, hasAll, contains, startsWith, endsWith **Read-only**: Yes #### \_id (\_id) **Description**: Folder ID. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, hasAll, contains **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Pricing Plans "PaidPlans/Plans" Collection Fields ## Article: "Plans" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-pricing-plans/wix-pricing-plans-paid-plans-plans-collection-fields.md ## Article Content: # Velo: Wix Pricing Plans "PaidPlans/Plans" Collection Fields
**Tips:** - Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md). - Before continuing, make sure you've read [About Pricing Plans](https://support.wix.com/en/article/pricing-plans-an-overview).
To use the Plans collection in code, refer to it as `"PaidPlans/Plans"`. ```javascript wixData.query("PaidPlans/Plans") .find() .then( (results) => { // handle the results } ); ``` This document describes the properties and fields in your Plans collection. ### Permissions The Plans collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): - **Read:** Anyone - **Create:** None - **Update:** None - **Delete:** None You cannot change the Plans collection's permissions. ### Fields > **Note:** > This app collection contains read-only fields that cannot be managed from the collection. You can update the fields from the relevant app in your site's [dashboard](https://support.wix.com/en/article/accessing-your-sites-dashboard). The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (_id) **Description**: The unique pricing plan ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, contains **Read-only**: Yes #### Role ID (roleId) **Description**: The role ID to assign to the buyer of the plan after purchase. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Name (name) **Description**: The name of the pricing plan. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Tagline (tagline) **Description**: The tagline for the pricing plan. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Ribbon (ribbon) **Description**: The ribbon available for this pricing plan. The ribbon lets you show additional information about the plan, such as "New!" **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Benefits (benefits) **Description**: Lists the benefits included in the plan as an array of strings. **Type**: Tags **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Price (price) **Description**: The price of the plan. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Currency (currency) **Description**: The plan's currency. Because plans have only one currency, this value is the same for all plans. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Recurring (recurring) **Description**: If the plan's payments are recurring (true) or one time only (false). **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Valid Until Canceled (validUntilCanceled) **Description**: If the plan is valid until the user explicitly cancels the payments (true). **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Period Unit (periodUnit) **Description**: The payment recurrence for the plan. Valid values are WEEK, MONTH, and YEAR. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Period Amount (periodAmount) **Description**: The plan is valid for this number of weeks, months, or years. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes #### Primary (primary) **Description**: Indicates if the plan is the primary plan. When viewing pricing plans on the site, the primary plan is highlighted with a customizable ribbon. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne **Read-only**: Yes #### Date Created (dateCreated) **Description**: Displays the date and time the plan was created. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, le, ge, lt, gt **Read-only**: Yes #### Date Updated (dateUpdated) **Description**: Displays the date and time the plan was last modified. **Type**: Date and Time **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, le, ge, lt, gt **Read-only**: Yes #### Display Index (displayIndex) **Description**: The index of the plan in the order it is displayed in Manage Plans from the site's dashboard. The index of the left-most plan on the top row has the value 1, the next one has the value 2, and so on. As time passes, if plans are deleted, the indexes of the remaining plans are not renumbered. They continue to be displayed in Manage Plans in the same numeric order. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, le, ge, lt, gt **Read-only**: Yes #### Slug (slug) **Description**: The plan's slug, which is its URL-friendly name that is unique across the site. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: No **Can be filtered**: Yes **Read-only**: Yes --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial: Using the Pricing Plans API for Pricing Plan Ordering and Payment ## Article: Using the Pricing Plans API for Pricing Plan Ordering and Payment ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-pricing-plans/tutorial-using-the-pricing-plans-api-for-pricing-plan-ordering-and-payment.md ## Article Content: # Velo Tutorial: Using the Pricing Plans API for Pricing Plan Ordering and Payment This article describes how you can use the [Velo Pricing Plans API](https://www.wix.com/velo/reference/wix-pricing-plans) to customize how you offer [pricing plans](https://support.wix.com/en/article/pricing-plans-an-overview) to your site's visitors. We're going to explain how we set up a sample site and the code we added to make it work. > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. The customization in this sample includes: * Working with dynamic pages and multiple collections. * Customizing the order and payment flow by adding popups. * Responding to the event of a plan being purchased. ### Overview **In our site we added the following:** * The Pricing Plans app.  * When adding the Pricing Plans app, a Plans collection with a list of membership plans for sale was automatically added.  * A collection for adding more details to each plan, such as testimonials and images. * A collection for tracking purchases. * A Collection & Repeater page. The repeater displays plans from the Testimonials collection, which references the Plans collection. Visitors can click a **See More** button for more details about a plan. * A Dynamic page. This page displays information from both the Plans collection and the Testimonials collection.  Visitors can click a **Buy Now** button to purchase a plan. * A popup that displays before processing with the purchase, and a different popup that displays after ordering a free plan. * An event for logging successful purchases in a collection. **Then we added code to do the following:** 1. When a visitor clicks the **See More** button, the button's click event handler calls the `to()` function to open the dynamic page. 2. When a visitor clicks the **Buy Now** button for a free plan, the button's click event handler calls the `createOnlineOrder()` function to order the plan and opens a congratulations popup. 3. When a visitor clicks the **Buy Now** button for a plan that costs money, the button's click event handler opens a confirmation popup. 4. When a visitor clicks **Yes**, the button's event handler calls the `createOnlineOrder()` function to order the plan. 5. The function checks if the visitor is logged in, and if not, prompts for login. 6. When the visitor is logged in, the function creates, and returns, a promise that resolves to an `order` object. 7. A payment procedure is initiated using the ID from the `order` object, causing a payment window to appear. 8. The visitor enters payment information and completes the transaction. This triggers the `onPlanPurchased()` event, which receives a `PlanPurchasedEvent` object. 9. The function logs an event in a collection using data from the `PlanPurchasedEvent` object. Here are the resources we created for this example: >**Note:** > This example demonstrates how, with Velo, we can _customize_ the membership plan flow. We intentionally did not use the standard Plans & Pricing page that is automatically created when adding the Pricing Plans app. ### Before You Start Before you start working with Wix pricing plans in code, make sure you do the following: * [Set up Wix Payments](https://support.wix.com/en/article/about-wix-payments) on your site. We also recommend that you understand the [typical payment process](https://www.wix.com/code/reference/wix-pay.html) before proceeding. * [Add the Pricing Plans app](https://support.wix.com/en/article/pricing-plans-adding-the-pricing-plans-app) to your site. ### Step 1: Set up Collections For this example, we added the Pricing Plans app to set up this collection:  * A PaidPlans/Plans collection, for membership plans. We manually created these collections: *  A Testimonials collection, for each plan's testimonials and images.  * A PlanEvents collection, for logging each successful purchase. >**Note:** > You may need to save/publish the site and refresh your browser to view collections. #### The PaidPlans/Plans Collection Adding the Wix Pricing Plans app to your site automatically creates the PaidPlans/Plans collection. This collection contains the basic fields necessary to work with pricing plans, such as the plan title, its price, its duration, its benefits, and so on. The Plans collection is read-only — plans can be created or modified on the Pricing Plans page in the site dashboard. This is what some of the data in our Plans collection looks like.  ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/258290dab4a8b038cc8274d7a66dd473.jpg) The plan title is saved in the **Name** field. Then, a field called **Slug** is created with similar but unique values. We used the **Slug** field to link to other collections.  #### Additional Collections You can [create additional collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) for additional plan details, such as testimonials and an image for each plan. In our new Testimonials collection, we added a PlanName [reference field](https://support.wix.com/en/article/displaying-content-from-multiple-database-collections-using-reference-fields) that references the **Slug** field in our Plans collection. The reference field links the testimonials and plan images to the corresponding plan in the Plans collection. This is what some of the data in our Testimonials collection looks like: ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b2216fbe05531bc2119d1a41281e0f40.jpg) #### PlanEvents Collection We created a collection to track successful purchases. We added a text field called **Data** to the collection. We created code in an **events.js** page to insert the live data from the `PlanPurchasedResult` object into the **Title** and **Data** fields. This code is explained in detail in Step 4. This is what some of the live data in our PlanEvents collection looks like, after we have made a few purchases.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/76096845-8b12-44f1-91f6-3dc2e838fdd9/2019/08/28/3ae026b7-8768-4426-8747-d6ac26cc8f48/b95fe517-eb55-49dc-bbc9-2fa5ac11d5c8.png)
### Step 2: Set up the Collection & Repeater Page We created a blank new page for displaying all the plans in our collection with a repeater and named it **Collection & Repeater**. #### Design the Page On the Collection & Repeater page, we added: * A [repeater](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids) to display the plans. * Text and image elements in the repeater to display the name, price, and picture of each plan. * A **See More** button in the repeater to display the selected plan using a dynamic page. (Instructions for creating the dynamic page are provided in Step 5.) * A dataset connected to our Testimonials collection for connecting data in the collection to the repeater.  Because the Testimonials collection has a reference field to our PaidPlans/Plans collection, all the data we need is accessible with this one dataset.
>**Note:** > When you [display data from a collection in a repeater](https://support.wix.com/en/article/how-to-display-database-content-in-a-repeater), you must first connect the repeater to the dataset, and then connect each element in the repeater to the dataset. #### Add Event Handlers * In our page's onReady( ) function, we added an [onClick](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/on-click.md) [event handler](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/about-event-handlers-in-wix.md) that will run when a **See More** button is clicked. #### Code the Page We added the following code to let the visitor select and view a specific plan.  ```javascript // For navigating to a dynamic page: import wixLocationFrontend from 'wix-location-frontend'; $w.onReady( function () { // If the site visitor clicks a plan's See More button, // the browser relocates to that plan's dynamic page. $w("#button1").onClick( (event) => { let $item = $w.at(event.context); let clickedItemData = $item("#dataset1").getCurrentItem(); let slug = clickedItemData.planName.slug; // We customize the URL of the dynamic Plans page to match this code. wixLocationFrontend.to('/gameplans/'+slug); } ); } ); ``` Alternatively, we could have connected the click action for our **See More** button to PlanName: Plans (Slug) in our dataset.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/7b865e92-14c2-4af1-bf32-e3c86be2dcbe/2022/12/19/b09924d3-6c14-4b78-9a44-5dfc5f9af81b/23dc19b9-c17d-45c9-9cd5-8c822d7cb87c.png)
### Step 3: Customize the Purchase of a Plan To demonstrate that we can customize the flow of a paid plan purchase, we will use the `createOnlineOrder()` function instead of the `startOnlinePurchase()` function. * With `createOnlineOrder()`, we can control and customize aspects of the order process. * With `startOnlinePurchase()`, we use the standard paid plan functionality as provided with the Wix Paid Plans app. The `startOnlinePurchase()` function provides a standard payment flow that starts with ordering and automatically continues to payment. This gives you less opportunity to customize, but requires less coding and design. As part of our customization, we added two popups to our site (to be used in Step 5). * The **Confirm** popup displays information about the currently-selected plan, and provides buttons for continuing or canceling the purchase. * The **Congrats** popup lets you know that you purchased a free plan. In this step, we kept it simple. However, this is the opportunity for you to really customize the process as you like.
### Step 4: Set up an Event Handler in the Backend to Log Purchases When a plan is successfully purchased or ordered, an [`onPlanPurchased`](https://dev.wix.com/docs/velo/apis/wix-pricing-plans-backend/events/on-plan-purchased.md) event is triggered. In our example, when this event is fired, we log the details about the purchase by inserting the information in our `PlanEvents` collection. Here is sample backend code that we put in an `events.js` file. ```javascript /***************************** * Backend code in events.js * *****************************/ // For inserting data into a collection. import wixData from 'wix-data'; // The onPlanPurchased() event is fired when a plan // is purchased, or a free plan is ordered. // Get the order information // from the event's order object. export function wixPricingPlans_onPlanPurchased(event) { // The PlanEvents collection has a title field, // in which we insert the type of transaction. // The collection also has a data field, // where we will insert information about the order // from the event's order object (json). if (event.order.price.amount === 0) { let orderData = { "title": "Free plan purchased", "data": event.order }; wixData.insert("PlanEvents", orderData); } else { let orderData = { "title": "Regular plan purchased", "data": event.order }; wixData.insert("PlanEvents", orderData); } } ``` ### Step 5: Customize your Dynamic Page Next, we created a new dynamic page for displaying each selected plan. We did this by going to our Plans collection in the Databases section of the Code sidebar and clicking Add Blank Dynamic Page. (If you use another method you will need to manually add the dataset for the PaidPlans/Plans collection.)
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/7b865e92-14c2-4af1-bf32-e3c86be2dcbe/2022/12/19/2ee1059a-1f86-447a-bf18-d67e3ac2b3ce/0d909389-e88a-4ff8-9c37-dc420d470385.png)
After we created the dynamic page, we [customized its URL](https://support.wix.com/en/article/creating-a-unique-dynamic-page-url) to end in "gameplans/{Slug}". #### Design the Page On this page, we added: * Text and image elements to display the details of each plan from the PaidPlans/Plans collection and the Testimonials collection. * A dataset for the Testimonials collection. The Testimonials collection is [filtered](https://support.wix.com/en/article/cms-displaying-content-from-multiple-collections-using-datasets) by planName so that any data displayed from the Testimonials collection matches the currently-displayed plan from the Plans collection. * A **Buy Now** button to order the plan.
#### Add Event Handlers * In the Properties & Events panel of the repeater, we added an `onItemReady` event handler that will run when the repeater is ready to be loaded. * In the Properties & Events panel of the `buyNow` button, we added an `onClick` event handler that will run when the **Buy Now** button is clicked.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/99f9f305-0415-4713-856e-b7aa93cbd4d3/2020/10/25/347b2402-9287-4bb6-ba8d-eb5689e71571/63ecbd24-2c29-43cb-94da-2de3d1fd14a6.png)
#### Code the Page The code on our dynamic page consists of five parts: * Imports for the APIs used in the rest of the code. * An `onReady()` event handler for making sure all page elements are available. * Retrieval of the current plan's data from the Plans collection in a `currentPlanObject` object. * Check if the visitor is logged in. If necessary, the site prompts the visitor to log in. * A function for processing the order of free plans and the purchase of plans that cost money. Let's take a look at the code one piece at a time.  At the end of this article, you can see the code in its entirety. **The Imports** We used the following APIs: * `wix-members-frontend` - For getting information about the current member and for logging members in and out. * `wix-window-frontend` - For popups. * `wix-pricing-plans-frontend` - For ordering and purchasing pricing plans. * `wix-pay-frontend` - For processing payments. So our importing code looks like this: ```javascript import wixWindowFrontend from 'wix-window-frontend'; import wixPayFrontend from 'wix-pay-frontend'; import {checkout} from 'wix-pricing-plans-frontend'; import {authentication} from 'wix-members-frontend'; ``` **`onReady()` Event Handler** In this example, there is no special setup we need to do for our elements. So we used the standard `onReady()` event handler. ```javascript $w.onReady( () => { ... ... //Insert code here. ... ... } ); ``` **`getCurrentItem()` Function** Next, we retrieved the current plan's data from the PaidPlans\\Plans collection using the Wix Dataset [`getCurrentItem()`](https://www.wix.com/code/reference/wix-dataset/dataset.html#getCurrentItem) function with the dynamic dataset on the page. We stored the information in a `currentPlanObject` object variable. Now we have access to the information we need about the plan to continue processing the order or purchase. If you copy the code snippet below, make sure to change the dataset name to match your own. ```javascript $w.onReady( () => { ... ... const currentPlanObject = $w("#dynamicDataset").getCurrentItem(); const planId = currentPlanObject._id; const planPrice = currentPlanObject.price; ... ... //Insert more code here. ... ... } ); ``` **`onClick()` Event Handler** Most of the logic in our page is contained in the `onClick()` event handler of the `buyNow` button. If you copy the code snippet below, make sure to change the button name to match your own. ```javascript $w.onReady( () => { ... ... const currentPlanObject = $w("#dynamicDataset").getCurrentItem(); const planId = currentPlanObject._id; const planPrice = currentPlanObject.price; ... ... $w('#buyNow').onClick((event) => { ... //Insert code for handling the button click here. ... }); ... ... //Insert more code here. ... ... } ); ``` **Check if Logged In** We don't want to sell plans to visitors that are not logged in. So we used `authentication.loggedIn()` to see if the visitor is logged in. * If the visitor is not logged in, we used `authentication.promptLogin()` function to ask the visitor to log in. Then we can call a function that we named `processPlan()` to continue. * If the visitor is already logged in, we can immediately call the `processPlan()` function. ```javascript $w.onReady( () => { const currentPlanObject = $w("#dynamicDataset").getCurrentItem(); const planId = currentPlanObject._id; const planPrice = currentPlanObject.price; $w('#buyNow').onClick((event) => { let isLoggedIn = authentication.loggedIn(); if (!isLoggedIn) { authentication.promptLogin().then(() => { processPlan(planId, planPrice); }) } else { processPlan(planId, planPrice); } }); }); ``` **Process the Order** Now let's look at the function we created, `processPlan()`, to get payment for the plan. We chose to use the [`createOnlineOrder()`](https://www.wix.com/velo/reference/wix-pricing-plans/checkout/createonlineorder) function for processing because we wanted to control and customize the entire order/purchase flow. When `orderPlan()` is called, and a plan is successfully purchased or ordered, an [`onPlanPurchased`](https://www.wix.com/velo/reference/wix-pricing-plans-backend/events/onplanpurchased) event is triggered. _This is where you can enter your own code to customize your own order/purchase flow._ In our example, we customized the flow by: * If the plan costs money, asking for member confirmation using the `Confirm` popup. If the member confirms, we call `createOnlineOrder()` and then process payment with the Wix Pay Frontend [`startPayment`](https://www.wix.com/code/reference/wix-pay.html#startPayment) function. * If the plan is free, using the `Congrats` popup to let the member know the plan order is successful. The order details are contained in the `orderObject` object. When copying this snippet, make sure to change the names of your popups to match your own. ```javascript function processPlan(myId, myPrice) { if (myPrice > 0) { checkout.createOnlineOrder(myId).then(orderObject => { wixWindowFrontend.openLightbox("Confirm", orderObject) .then((goForIt) => { if (goForIt) { wixPayFrontend.startPayment(orderObject.wixPayOrderId); } }); }) } else { checkout.createOnlineOrder(myId).then(orderObject => { wixWindowFrontend.openLightbox("Congrats", orderObject); }) } } ``` **All the Code** The code in its entirety looks like this:  ```javascript import wixWindowFrontend from 'wix-window-frontend'; import wixPayFrontend from 'wix-pay-frontend'; import {checkout} from 'wix-pricing-plans-frontend'; import {authentication} from 'wix-members-frontend'; $w.onReady(function () { const currentPlanObject = $w("#dynamicDataset").getCurrentItem(); const planId = currentPlanObject._id; const planPrice = currentPlanObject.price; $w('#buyNow').onClick((event) => { let isLoggedIn = authentication.loggedIn(); if (!isLoggedIn) { authentication.promptLogin().then(() => { processPlan(planId, planPrice); }) } else { processPlan(planId, planPrice); } }); }); function processPlan(myId, myPrice) { if (myPrice > 0) { checkout.createOnlineOrder(myId).then(orderObject => { wixWindowFrontend.openLightbox("Confirm", orderObject) .then((goForIt) => { if (goForIt) { wixPayFrontend.startPayment(orderObject.wixPayOrderId); } }); }) } else { checkout.createOnlineOrder(myId).then(orderObject => { wixWindowFrontend.openLightbox("Congrats", orderObject); }) } } ``` **Tip**: Want to use the standard, "out of the box" Wix flow for letting visitors purchase a plan? * Use the [`startOnlinePurchase()`](https://www.wix.com/velo/reference/wix-pricing-plans/checkout/startonlinepurchase) function in the code instead of the `createOnlineOrder()` function. * No need to call `wixPayFrontend`. The `startOnlinePurchase( )` function does that for you. ### API List The following APIs are used in the code in this article. To learn more, see the [API Reference](https://www.wix.com/code/reference/). * [wix-pricing-plans-frontend](https://www.wix.com/velo/reference/wix-pricing-plans) \- For ordering and purchasing pricing plans. * [wix-pay-frontend](https://www.wix.com/velo/reference/wix-pay.html) - For payments. * [wix-members-frontend](https://www.wix.com/velo/reference/wix-members) - Gets information about the current member and for logging members in and out. * [wix-data](https://www.wix.com/code/reference/wix-data.html) - For inserting into collections. * [wix-location-frontend](https://www.wix.com/code/reference/wix-location.html) - For navigation. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Custom Pricing Plans Page ## Article: Build a custom Pricing Plans page ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-pricing-plans/build-a-custom-pricing-plans-page.md ## Article Content: # Velo: Build a Custom Pricing Plans Page You can replace your site’s default [Wix Pricing Plans page](https://support.wix.com/en/article/customizing-the-design-of-your-pricing-plans-page) with your own customized version. This enables you to modify or extend the page’s functionality to suit your precise business needs. To integrate the page into your site’s pricing plans flow and implement its functionality, you need to add code to the page and use Velo APIs. Your code has to do several things: - Get data about the available pricing plans. - Handle UI initialization and interactions. - Implement any customized business logic. - Direct users to the next page in the flow. ## Step 1 | Add a custom Pricing Plans page to your site To create a custom Pricing Plans page, do one of the following:
Wix Studio 1. Click ![code-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9f782f2d59ce3d9927c773d706c594b5) and then **Start Coding** from the sidebar on the left side of the Editor. 1. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5e1b7a15094cb91f7f21d436b1d900bc) on the sidebar. 1. On the **Plans & Pricing** (under **Pricing Plans Pages**), click the **More Actions** icon ![more-actions-studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f9f5b48e5e4576cf829bcd65b64f81a3). 1. Click **Replace with custom page**. 1. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b7dd4ec084c55a0517d8d0b18696ea8c)
Wix Editor 1. Turn on [Dev Mode](https://support.wix.com/en/article/about-velo-by-wix#to-enable-velo-on-your-site). 1. Click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/aa32565a982895a1f9a0db2c99522b91) on the left side of the editor. 1. Click **Pricing Plans Pages**, and then on the **Plans & Pricing** page, click ![ellipsis-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f54b38a3f22af34b0a705770ebee01ac). 1. Click **Replace with custom page**. 1. In the confirmation panel, click **Replace**. ![replace-classic](https://wixmp-833713b177cebf373f611808.wixmp.com/images/686fd1d1a85f32ac558fe952a5537c04)
## Step 2 | Design your custom page [Add elements to the page](https://support.wix.com/en/article/wix-editor-adding-and-deleting-elements) to create your business’s customized design and functionality, such as text, buttons, and images. At a minimum, these elements must: - Display information about the pricing plans and let the site visitor select the plan. - Provide an action button that navigates to the next page in the flow (usually the Checkout). ## Step 3 | Add Velo code to your page 1. Use the [`getAppPageData()`](https://www.wix.com/velo/reference/wix-window-frontend/getapppagedata) function to get the [plans object](https://dev.wix.com/docs/velo/api-reference/wix-window-frontend/app-page-data.md) associated with the page, and the [`getPricingPageOptions()`](https://dev.wix.com/docs/velo/api-reference/wix-pricing-plans-frontend/custom-purchase-flow/get-pricing-page-options.md) function to get the options set for the current Plans & Pricing page. For example: ``` js import wixWindowFrontend from 'wix-window-frontend'; $w.onReady(async function () { const { plans } = await wixWindowFrontend.getAppPageData(); const options = await customPurchaseFlow.getPricingPageOptions(); }); ``` 2. Depending on the functionality you’re developing, implement any business logic that your customized page requires. For example, define all the items in the repeater, such as text and buttons. 3. Add an [event handler](https://support.wix.com/en/article/velo-reacting-to-user-actions-using-events) to the page’s action button so that it navigates to the next page in the flow, which is typically your site’s Checkout. Use the [`navigateToCheckout()`](https://dev.wix.com/docs/velo/api-reference/wix-pricing-plans-frontend/custom-purchase-flow/navigate-to-checkout.md) function and pass the checkout options together with the ID of the plan to buy. The repeater displays plans on the page. For example: ```js $w("#repeater").onItemReady(($item, plan) => { $item("#title").text = plan.name; $item("#button").onClick(() => { customPurchaseFlow.navigateToCheckout({ planId: plan._id, ...options.checkout, }); }); }); $w('#repeater').data = plans; ``` Your custom page can now display the pricing plan’s data and direct site visitors to the next page in the flow. ## Test your code To receive a populated page data object using `wixWindowFrontend.getAppPageData()` when [testing your code](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/testing-monitoring/testing-troubleshooting/testing-and-troubleshooting-your-code.md), do the following: 1. Create at least 1 pricing plan. 1. Publish a [test site](https://support.wix.com/en/article/about-test-sites). 1. Navigate to the Pricing Plans page in your test site. 1. Return to the editor. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Portfolio "Project Items" Collection Fields ## Article: Wix Portfolio "Project Items" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-portfolio/wix-portfolio-project-items-collection-fields.md ## Article Content: # Velo: Wix Portfolio "Project Items" Collection Fields >**Note:** > This collection contains read-write fields.
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the ProjectItems collection in code, refer to it as `"Portfolio/ProjectItems"`. ```javascript wixData.query("Portfolio/ProjectItems") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your ProjectItems collection.
**Important:** Trying to query more than 100 items exceeds the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) and results in an error.
### Permissions The Project Items collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone  * **Create:** Admin * **Update:** Admin * **Delete:** Admin You cannot change the Project Items collection's permissions.  ### Fields The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### Project (project) **Description**: The project ID that contains the project item. **Type**: Reference **Can connect to data**: Yes, to the referenced item's field. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq **Read-write**: Yes **Required**: Yes #### ID (\_id) **Description**: The project item ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, hasSome **Read-only**: Yes **Required**: No #### Sort Order (sortOrder) **Description**: The project items sort order. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No #### Title (title) **Description**: The title of the project item. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No #### Description (description) **Description**: The description of the project item. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No #### Type (type) **Description**: The project item's type. **Type**: Text **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No **Note:** The type value is one of: * "IMAGE" * "VIDEO" #### Created Date (\_createdDate) **Description**: The date the project item was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Updated Date (\_updatedDate) **Description**: The date the project item was last updated. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Image (image) **Description**: A JSON object that represents a project item's image. **Type**: Object **Can connect to data**: Yes by the image's focal points. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json { "imageInfo": "wix:image://v1/8bb438_4400b236f09943f3a60eaf257d0b67dd~mv2.jpg/8bb438_4400b236f09943f3a60eaf257d0b67dd~mv2.jpg#originWidth=2400&originHeight=3600", "focalPoint": { "x": 0.5, "y": 0.5 } } ``` #### Video (video) **Description**: A JSON object that represents a project item's video. **Type**: Object **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No **Note:** Currently not supported in the Wix Editor. ```json { "videoInfo": "wix:video://v1/162e66_1e344b4233a5430c91d8be1310ac90a8/vecteezy_the-footage-animation-of-countdown-timer-from-5-seconds__666.mp4#posterUri=162e66_1e344b4233a5430c91d8be1310ac90a8f002.jpg&posterWidth=1920&posterHeight=1080", "durationInMillis": 6 } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Portfolio "Projects" Collection Fields ## Article: Wix Portfolio "Projects" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-portfolio/wix-portfolio-projects-collection-fields.md ## Article Content: # Velo: Wix Portfolio "Projects" Collection Fields >**Note:** > This collection contains read-write fields.
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Projects collection in code, refer to it as `"Portfolio/Projects"`. ```javascript wixData.query("Portfolio/Projects") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Projects collection.
**Important:** Trying to query more than 100 items exceeds the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) and results in an error.
### Permissions The Projects collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** Admin * **Update:** Admin * **Delete:** Admin You cannot change the Projects collection's permissions.  ### Fields The field name is listed as the heading of each section, with the field key listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The project ID created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-only**: Yes **Required**: No #### Revision (revision) **Description**: The current state of a project in the collection. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Title (title) **Description**: The title of the project. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-write**: Yes **Required**: No #### Description (description) **Description**: The description of the project. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-write**: Yes **Required**: No #### Hidden (hidden) **Description**: Whether the project is hidden. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-write**: Yes **Required**: No #### Collection Ids (collectionIds) **Description**: Array of collection IDs that the project belongs to. **Type**: Array of Strings **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome, hasAll **Read-write**: Yes **Required**: No #### Details (details) **Description**: The details of the project. **Type**: Array of Objects **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json [ { "label": "Project Type", "text": "Photography" }, { "label": "Date", "text": "April 2023" } ] ``` #### Slug (slug) **Description**: The project's slug, which is its URL-friendly name that is unique across the portfolio. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-write**: Yes **Required**: No #### Created Date (\_createdDate) **Description**: The date the project was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, gt, lt **Read-only**: Yes **Required**: No #### Updated Date (\_updatedDate) **Description**: The date the project was last updated. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, gt, lt **Read-only**: Yes **Required**: No #### Url (url) **Description**: The url of the project page. **Type**: URL **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Seo Data (seoData) **Description**: A JSON object that represents Seo data of the project. **Type**: Object **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json { "settings": { "preventAutoRedirect": "true" }, "tags": { [ "children": "", "custom": "", "disabled": "", "meta": {}, "props": {}, "type": "" ] } } ``` #### Synced Project (syncedProject) **Description**: Indicates whether the project is synced with an external platform. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No **Note**: Synced projects are updated from their external platform on a daily basis. #### Cover Image (coverImage) **Description**: A JSON object that represents a project's cover image. **Type**: Object **Can connect to data**: Yes, by the cover image's focal points. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json { "imageInfo": "wix:image://v1/8bb438_4400b236f09943f3a60eaf257d0b67dd~mv2.jpg/8bb438_4400b236f09943f3a60eaf257d0b67dd~mv2.jpg#originWidth=2400&originHeight=3600", "focalPoint": { "x": 0.5, "y": 0.5 } } ``` #### Cover Video (coverVideo) **Description**: A JSON object that represents a project's cover video. **Type**: Object **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No **Note:** Currently not supported in the Wix Editor. ```json { "videoInfo": "wix:video://v1/162e66_1e344b4233a5430c91d8be1310ac90a8/vecteezy_the-footage-animation-of-countdown-timer-from-5-seconds__666.mp4#posterUri=162e66_1e344b4233a5430c91d8be1310ac90a8f002.jpg&posterWidth=1920&posterHeight=1080", "durationInMillis": 6 } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Portfolio "Collections" Collection Fields ## Article: Wix Portfolio "Collections" Collection Fields ## Article Link: https://dev.wix.com/docs/develop-websites/articles/wix-apps/wix-app-collections/other-apps/wix-portfolio/wix-portfolio-collections-collection-fields.md ## Article Content: # Velo: Wix Portfolio "Collections" Collection Fields >**Note:** > This collection contains read-write fields.
**Tip:** Before continuing, make sure you've read [Working with Wix App Collections](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/collections/working-with-wix-app-collections-and-code.md).
To use the Collections collection in code, refer to it as `"Portfolio/Collections"`. ```javascript wixData.query("Portfolio/Collections") .find() .then( (results) => { // handle the results } ); ``` This document describes the permissions and fields in your Collections collection.
**Important:** Trying to query more than 100 items exceeds the wix-data [limit](https://www.wix.com/corvid/reference/wix-data.WixDataQuery.html#limit) and results in an error.
### Permissions The Collections collection has the following [permissions](https://support.wix.com/en/article/about-collection-permissions): * **Read:** Anyone * **Create:** Admin * **Update:** Admin * **Delete:** Admin You cannot change the Collections collection's permissions.  ### Fields The field name is listed as the heading of each section, with the field ID listed in parentheses, like this: Name (name). The fields appear in this document in the order in which they appear in your collection by default. #### ID (\_id) **Description**: The collection ID that was created by the server. This is a system field and is hidden by default. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-only**: Yes **Required**: No #### Revision (revision) **Description**: The current state of an item in the collection. The revision's state changes every time an item is modified. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Title (title) **Description**: The title of the collection. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-write**: Yes **Required**: No #### Description (description) **Description**: The description of the collection. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome **Read-write**: Yes **Required**: No #### Slug (slug) **Description**: The collection item's slug. The slug is the URL-friendly name that is unique across the portfolio. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, startsWith **Read-write**: Yes **Required**: No #### Cover Image (coverImage) **Description**: A JSON object that represents the cover image of the collection. **Type**: Object **Can connect to data**: Yes, by the cover image's focal points. **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json { "imageInfo": "wix:image://v1/8b7eef_3deb121802514be7b698d0447559f9da~mv2.png/avatar-ga890ac591_1280.png#originWidth=1280&originHeight=1280", "focalPoint": { "x": "number", "y": "number" } } ``` #### Hidden (hidden) **Description**: Whether the collection is hidden. **Type**: Boolean **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: eq, ne, hasSome **Read-write**: Yes **Required**: No #### Sort Order (sortOrder) **Description**: The collection sort order. **Type**: Number **Can connect to data**: Yes **Can use in dynamic page URL**: Yes **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, le, gt, ge **Read-write**: Yes **Required**: No #### Created Date (\_createdDate) **Description**: The date the collection was created. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, gt **Read-only**: Yes **Required**: No #### Updated Date (\_updatedDate) **Description**: The date the collection was last updated. This is a system field and is hidden by default. **Type**: Date **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: Yes **Can be filtered**: eq, ne, hasSome, lt, gt **Read-only**: Yes **Required**: No #### Url (url) **Description**: The url of the collection page. **Type**: Text **Can connect to data**: Yes **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-only**: Yes **Required**: No #### Seo Data (seoData) **Description**: A JSON object that represents Seo data of the collection. **Type**: Object **Can connect to data**: No **Can use in dynamic page URL**: No **Can be sorted**: No **Can be filtered**: No **Read-write**: Yes **Required**: No ```json { "settings": { "preventAutoRedirect": "true" }, "tags": { [ "children": "", "custom": "", "disabled":"", "meta": {}, "props": {}, "type":"" ] } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Caching ## Article: About Caching ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md ## Article Content: # About Caching Caching is a method used to temporarily store data in a storage location on Wix's infrastructure. With caching, you can keep frequently accessed content readily available, reducing the time it takes to retrieve information on your site and enhancing user experience. It's important to manage caching effectively to prevent site visitors from seeing outdated or incorrect content. Implementing caching can enhance the performance of your Wix site in various areas. Below are the key applications of caching: - [Page caching](#page-caching) - [Web method caching](#web-method-caching) - [Router caching](#router-caching) - [Data caching](#data-caching) ## Page caching While most site pages are automatically cached, you can manually control the cache settings according to your site's needs. Learn more [about page caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-page-caching.md). ## Web method caching By implementing web method caching in your backend code, you can limit how often your backend API calls execute. Learn more [about web method caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-web-method-caching.md). ## Router caching Router caching helps to reduce the need to fetch page data repeatedly each time a site visitor browses to a page handled by a router. Learn more [about router caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-router-caching.md). ## Data caching Data caching significantly speeds up data queries. Wix automatically caches the results of certain data queries, so when a visitor makes a query whose results are cached, Wix provides the cached results instead of querying the database again. Learn more [about caching data query results](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/data-api/about-caching-data-query-results.md). ## See also - [Control Page Caching Manually](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/control-page-caching-manually.md) - [Implement Web Method Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-web-method-caching.md) - [Implement Router Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-router-caching.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Page Caching ## Article: About Page Caching ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-page-caching.md ## Article Content: # About Page Caching When a page on your site is requested for the first time, Wix caches the rendered page on a Content Delivery Network (CDN). The next time someone requests this page, Wix serves the cached version instead of rendering the page's elements and assets from scratch. Wix allows you to manage and disable caching for your site pages; however, the more pages on your site that are cached, the faster the overall experience is for your visitors.
__Important:__ All pages on your site are cached automatically, except for those accessed by logged-in members. If a visitor is logged into your site, any page they open isn't cached and is always rendered from scratch. It's not possible to enable caching for logged-in member sessions.
## Supported IDEs You can manually control the lifespan of the cache and disable caching in: - The editor (Wix Studio and Wix Editor). You can invalidate your caches manually using [code](https://dev.wix.com/docs/velo/api-reference/wix-site-backend/invalidate-cache.md) in: - The editor (Wix Studio and Wix Editor) - The Wix IDE - Your local IDE ## Cache invalidation By default, page caches are cleared or invalidated after 1 week. Additionally, page caches are automatically invalidated whenever you publish your site or when application data changes. For example, if you update the product catalog in the Stores app, the page cache is cleared, and the page is then re-cached the next time a site visitor requests the page. In situations where your page's data is updated infrequently, such as when your code fetches data from an external collection, you can invalidate the cache using the [`wix-site-backend`](https://dev.wix.com/docs/velo/api-reference/wix-site-backend/invalidate-cache.md) API. This ensures that visitors don't see outdated information on your site. ## When to control or disable caching manually Wix allows you to manage caching for your site pages. This means that you can determine how often to refresh page caches or disable caching for a page entirely. When managing page caching, it's important to consider how pages may appear differently to various site visitors. You should consider manually controlling page caching for: - Pages that retrieve data from external sources, such as product availability from an inventory management system. - Pages that display frequently changing data, like exchange rates or stock market details. - Pages that customize the display of elements based on a site visitor's geo-location, such as currency or time zone. - Pages that automatically update the date or time on a page to reflect real-time changes. These types of pages require Wix's servers to render the changes to provide the most current information. If automatic caching is enabled, visitors might initially see outdated content because the page is served from a cached version, rather than being freshly generated. For example, if the code on your page fetches exchange rates from an external resource and displays them in a TextBox element, visitors may see outdated rates from the cached version before the new rates are fetched and rendered. This can cause a temporary "flicker" effect as the page data updates. If your page's content only updates occasionally, you can keep caching enabled and invalidate the cache in your code whenever the data is updated. Properly controlling caching is essential for delivering accurate information and ensuring a smooth experience for your site visitors. > **Note:** > Code added to your site's `masterPage.js` file runs on every page of your site. This code can affect how each page is displayed and may cause incorrect content to be shown if a page is cached. If you need to We recommend checking your `masterPage.js` file and only including code that must run on every page. Move other code to the appropriate page files. ## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [Control Page Caching Manually](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/control-page-caching-manually.md) - [Webinar: Planning for scale: large data design considerations](https://youtu.be/BGnpQ_LTHPg?feature=shared&t=1464) - [`wix-site-backend` API](https://dev.wix.com/docs/velo/api-reference/wix-site-backend/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Control Page Caching Manually ## Article: Control Page Caching Manually ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/control-page-caching-manually.md ## Article Content: # Control Page Caching Manually Sometimes caching can cause site visitors to see outdated or incorrect content. For such cases you can manually control or disable caching for your site pages. You can manually control page caching by: - Disabling page caching in the editor. - Setting a lifespan for page caches in the editor. - Using code to invalidate page caches when necessary. ## Disable caches or manage the cache lifespan in the editor If the data on your site page changes very frequently, you can disable caching for that page, ensuring your site is always displaying the most up-to-date information. If the data on your page updates regularly, you can set your page's cache to clear and refresh at an interval that matches your needs. To disable or manage caching: 1. In the editor, click the **Pages** ![pages icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/200b9d74a1d550b51058678b2df28b0b.png) icon on the left sidebar to open the Site Pages panel. 1. Click the **More Actions** ![show more](https://wixmp-833713b177cebf373f611808.wixmp.com/images/506abdf364c2f86a8f34670cbcac015b.png) icon for the page you want to control caching, and then select **Settings**. This opens the Page Settings panel for that page. 1. Click **Advanced Settings** and turn on the **Manually control caching for this page** toggle. 1. Open the **How often do you want to reset this page's cache?** dropdown. - To disable caching for the page, select **Never (disable caching)**. - To manage the cache lifespan for the page, select the time interval that best suits your page's needs.
## Invalidate cache with code If your page’s data only updates occasionally, you can keep automatic caching enabled and add code to the page to invalidate the cache when the page's data is expected to be updated. Call the [`invalidateCache()` method](https://dev.wix.com/docs/velo/apis/wix-site-backend/invalidate-cache.md) from the `wix-site-backend` module in your backend code. ```js import { Permissions, webMethod } from "wix-web-module"; import wixSiteBackend from "wix-site-backend"; export const invalidateCache = webMethod(Permissions.Anyone, () => { return wixSiteBackend.invalidateCache(); }); ```
__Important:__ The `invalidateCache()` method invalidates the cache for the whole site, not just the page where you run the code.
## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [About Page Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-page-caching.md) - [Webinar: Planning for scale: large data design considerations](https://youtu.be/BGnpQ_LTHPg?feature=shared&t=1464) - [wix-site-backend API](https://dev.wix.com/docs/velo/apis/wix-site-backend/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Web Method Caching ## Article: About Function Caching in the Backend ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-web-method-caching.md ## Article Content: # About Web Method Caching Web method caching significantly enhances your site's performance by temporarily storing frequently-accessed data—specifically, the return values of your backend API calls. This allows subsequent requests for the same data to be served quickly from the cache stored on Wix's infrastructure, reducing the need to re-execute the logic each time the request is made. You can set your backend methods to cache return values for a specific duration or Time To Live (TTL). As a result, the next time a visitor makes a request that triggers your backend methods, the cached data is served immediately until the TTL expires or the cache is invalidated. Caching improves overall performance by reducing server load, decreasing response times, and minimizing resource consumption. Additionally, it provides reliability, as caches can serve as backups if the original data source is temporarily unavailable. > **Note:** The web methods in your code aren't cached automatically. ## Supported IDEs You can implement web method caching in your web modules using: - The editor (Wix Studio and Wix Editor) - The Wix IDE - Your local IDE ## When to use web method caching Web method caching is most beneficial in the following scenarios: - When there are numerous requests for the same data, such as a product catalog or popular blog posts. - When the data is mostly static or seldom changes, like company profiles, fixed schedules, or images. - When your backend methods involve heavy computations or extensive database queries that can strain server resources. Conversely, web method caching may not always be the best solution in situations where your backend methods: - Return real-time data that requires immediate accuracy, such as live stock prices or sports scores. - Return sensitive or personalized data, like user account details and preferences. - Require fresh data for each operation, as in the case of processing payments. In cases where real-time updates are essential, consider implementing caching selectively. For example, if a backend method retrieves foreign exchange rates, it may be valuable to cache that data for a 24-hour period, allowing it to refresh on a daily basis. For data that changes less frequently, you can set a longer TTL period; otherwise, web method caches have a default TTL of 1 week. ## Understanding cache invalidation Cache invalidation is a vital process that ensures the accuracy and relevance of the data being presented to site visitors. It involves clearing cached return values when the underlying data changes. Cached return values are cleared when: - The cache's TTL expires. - You republish your site after updating code. - You manually implement cache invalidation in your code. You can implement cache invalidation in your code using the `invalidateCache()` method from `wix-cache-backend`. This should be done when there are significant changes to your site's content, such as updates in product availability or the creation of a new blog post. By invalidating caches, you maintain data integrity, prevent site visitors from accessing outdated information, and optimize your site's performance and responsiveness. ## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [Implement Web Method Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-web-method-caching.md) - [About Web Modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) - [wix-web-module API](https://dev.wix.com/docs/velo/apis/wix-web-module/introduction.md) - [wix-cache-backend API](https://dev.wix.com/docs/velo/apis/wix-cache-backend/cache/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Implement Web Method Caching ## Article: Implement Web Method Caching ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-web-method-caching.md ## Article Content: # Implement Web Method Caching [Web method caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-function-caching-in-the-backend.md) is an effective way to improve the performance of your site by temporarily storing the return values of web methods in a cache. This article guides you through the process of implementing web method caching and how to invalidate it when necessary. ## Step 1 | Set up caches You must assign identifiers to cached return values of your web methods and can optionally define a cache's Time To Live (TTL). These required identifiers, called tags, allow you to easily identify caches that may need invalidation. For example, you can invalidate all caches that have the tag "math." To set up caching for your web methods: 1. Open your [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) and find the methods you want to cache. 1. For each web method that you want to cache, update the code so that the [`webMethod()`](https://dev.wix.com/docs/velo/api-reference/wix-web-module/web-method.md) is called with the following `options`: - `cache`: An object containing: - `tags`: One or more identifiers of the cached return value. - `ttl`: The cache's time-to-live in seconds. If omitted, defaults to 1 week (`604800` seconds). ```js import { Permissions, webMethod } from "wix-web-module"; export const cachedMultiply = webMethod(Permissions.Anyone, (a, b) => a * b, { cache: { tags: ["multiply", "math"], ttl: 604800, }, }); ```
__Important:__ The `tags` field is required for caching. If omitted, nothing is cached.
## Step 2 | Invalidate caches Invalidating or clearing your caches causes your backend methods to re-execute upon the following request, ensuring that fresh data is retrieved and cached. To invalidate caches, use the `invalidateCache()` method from the [`wix-cache-backend`](https://dev.wix.com/docs/velo/api-reference/wix-cache-backend/cache/invalidate-cache.md) module. This method accepts the `invalidationMethods` parameter, which specifies the tags previously assigned to the caches you want to invalidate.
__Important:__ - You must specify `invalidationMethods` when invalidating your cache. - A cache can be assigned multiple tags. Any cache with at least one tag specified in the `invalidationMethods` object is invalidated when `invalidateCache()` is called. - The `invalidateCache()` method must be [elevated](https://dev.wix.com/docs/velo/api-reference/wix-auth/elevate.md) and wrapped in a [web method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#web-methods).
To invalidate caches: 1. Add the necessary imports in your web modules (backend files). ```js import { Permissions, webMethod } from "wix-web-module"; import { elevate } from "wix-auth"; import { cache } from "wix-cache-backend"; ``` 1. Create a variable to store the tags assigned to the cache. Then call `elevate()` and store its returned value in another variable. ```js const invalidationMethods = [{ tag: "math" }, { tag: "multiply" }]; const elevatedInvalidateCache = elevate(cache.invalidateCache); ``` 1. Call `webMethod()` and store its return value in an exported variable. Pass a permissions value and the elevated invalidation method. ```js export const invalidateCache = webMethod(Permissions.Admin, async () => { try { await elevatedInvalidateCache(invalidationMethods); console.log("Successfully invalidated cache."); } catch (error) { console.error(error); } }); ``` > **Notes:** > - The `wix-cache-backend` API, allows you to invalidate web method caches as well as [router caches](https://dev.wix.com/docs/velo/api-reference/wix-router/ok.md). > - To invalidate the server side rendering (SSR) cache of your site, refer to the [`invalidateCache()`](https://dev.wix.com/docs/velo/api-reference/wix-site-backend/invalidate-cache.md) method in the `wix-site-backend` module. ## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [About Web Method Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-web-method-caching.md) - [About Web Modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md) - [wix-web-module API](https://dev.wix.com/docs/velo/api-reference/wix-web-module/introduction.md) - [wix-cache-backend API](https://dev.wix.com/docs/velo/api-reference/wix-cache-backend/cache/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Router Caching ## Article: About Router Caching ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-router-caching.md ## Article Content: # About Router Caching Router caching can significantly enhance your site's performance. When a site visitor successfully navigates to a page handled by a router, the router processes the request and retrieves the corresponding router page data. The response, which includes this data, is sent back to the visitor using the `ok()` method in the [`wix-router`](https://dev.wix.com/docs/velo/api-reference/wix-router.md) module. With router caching implemented, the return value of the `ok()` method — which contains the same router page data — is temporarily stored on Wix's infrastructure. This allows for immediate retrieval of the cached router page data the next time the same page is requested, reducing the need for the full router and rendering process, and ultimately improving load times for subsequent visitors. You can manage caches by specifying a duration, known as Time To Live (TTL), which defines how long the return values should be stored. Additionally, you can assign identifying tags to the cache, allowing for precise cache invalidation when necessary. Caching improves overall performance by delivering content faster, reducing the server load, and enabling your site to accommodate more concurrent visitors. Caches also serve as a reliable backup if the original data source becomes temporarily unavailable. > **Note:** Router pages, unlike regular site pages, aren't cached automatically. ## Supported IDEs You can implement router caching using: - The editor (Wix Studio and Wix Editor) - The Wix IDE - Your Local IDE ## When to use router caching Router caching is particularly beneficial for: - Pages that receive high traffic. - Pages with dynamic content that infrequently changes. For instance, in an online department store, when a site visitor first accesses the "Dresses" category (located at `clothing/womens-clothing/dresses`), the page data is retrieved through the router, cached, and then rendered. As a result, subsequent visitors requesting the "Dresses" page experience significantly reduced loading times. Similarly, for router pages like popular articles on a news website, caching helps efficiently handle high view counts. Conversely, router caching is less effective for requests that depend on: - **Geo-location**: Responses that vary based on a visitor's location. - **Specific filters**: Requests that involve personalized filtering options. - **Search queries**: Responses tailored to a visitor's specific search terms. The personalized nature of these scenarios makes caching less practical. For example, caching the router response for daily weather updates on a page for a visitor in London would be irrelevant for someone in California. Additionally, caching responses based on unique user filters or search terms is inefficient, as traffic for those exact queries is generally minimal. ## Understanding cache invalidation Cache invalidation is important for maintaining the accuracy and freshness of your site. It involves clearing caches to allow for the retrieval of updated content before the cache is renewed. Router caches are cleared when: - The cache's TTL expires. - You republish your site after updating code. - You manually implement cache invalidation in your code. To invalidate caches in your code, you can use the `invalidateCache()` method from `wix-cache-backend`. This should be done when the content on your router page changes or when the page displays time sensitive information, such as updates in a clothing catalog, or weekly updates to a calendar. By invalidating caches, you maintain data integrity, prevent site visitors from accessing outdated information, and optimize your site's performance and responsiveness. ## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [Implement Router Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-router-caching.md) - [About Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) - [Create a Router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) - [wix-router API](https://dev.wix.com/docs/velo/api-reference/wix-router/introduction.md) - [wix-cache-backend API](https://dev.wix.com/docs/velo/api-reference/wix-cache-backend/cache/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Implement Router Caching ## Article: Implement Router Caching ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/implement-router-caching.md ## Article Content: # Implement Router Caching Router caching in an effective strategy to improve your site's performance by temporarily storing the return values from the `ok()` method, which contain the same page data that is sent as the response to a router request. When visitors access a router page, the system can quickly deliver the cached data, enabling faster page loads and a better overall user experience. This article guides you through the process of implementing router caching and how to invalidate it when necessary. ## Step 1 | Set up your cache You must assign identifiers to router caches and can optionally define a cache's Time To Live (TTL). These required identifiers, called tags, allow you to easily identify caches that may need invalidation. For example, you can invalidate all caches that have the tag "dress-page." To set up caching for your routers: 1. Open the [router.js](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/overview/where-do-i-put-my-code.md#where-do-i-put-router-code) file in your backend code and find the routers you want to cache. 1. In the request for each router page you want to cache, create a variable representing the `options` parameter to store the `cache` object. Include the following fields in the `cache` object: - `tags`: One or more identifiers of the cached return value. - `ttl`: The cache's time-to-live in seconds. If omitted, defaults to 1 week (`604800` seconds). 1. In the return statement of the router request, pass the variable holding the `cache` object to the [`ok()` method](https://dev.wix.com/docs/velo/api-reference/wix-router/ok.md). ```js import { ok } from "wix-router"; export function myRouter_Router(request) { let headOptions = {}, let cacheOptions = { cache: { tags: ["routerPage"], ttl: 518400 } }; return ok("router-page", dataObj, headOptions, cacheOptions); } ```
__Important:__ The `tags` field is required for caching. If you omit `tags`, nothing is cached.
## Step 2 | Invalidate caches Invalidating or clearing your caches triggers the routing and rendering process upon the following next, ensuring that your caches hold the most up-to-date page data. To invalidate caches, use the `invalidateCache()` method from the [`wix-cache-backend`](https://dev.wix.com/docs/velo/api-reference/wix-cache-backend/cache/invalidate-cache.md) module. This method accepts the `invalidationMethods` parameter, which specifies the tags previously assigned to the caches you want to invalidate.
__Important:__ - You must specify `invalidationMethods` when invalidating your cache. - A cache can be assigned multiple caches. Any cache with at least one tag specified in the `invalidationMethods` object will be invalidated when `invalidateCache()` is called. - The `invalidateCache()` method must be [elevated](https://dev.wix.com/docs/velo/api-reference/wix-auth/elevate.md) and wrapped in a [web method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#web-methods).
To invalidate caches: 1. Add the necessary imports in your backend files. ```js import { Permissions, webMethod } from "wix-web-module"; import { elevate } from "wix-auth"; import { cache } from "wix-cache-backend"; ``` 1. Create a variable to store the tags assigned to the cache. Then call `elevate()` and store its returned value in another variable. ```js const invalidationMethods = [{ tag: "math" }, { tag: "multiply" }]; const elevatedInvalidateCache = elevate(cache.invalidateCache); ``` 1. Call `webMethod()` and store its return value in an exported variable. Pass a permissions value and the elevated invalidation method. ```js export const invalidateCache = webMethod(Permissions.Admin, async () => { try { await elevatedInvalidateCache(invalidationMethods); console.log("Successfully invalidated cache."); } catch (error) { console.error(error); } }); ``` > **Notes:** > - The `wix-cache-backend` API, allows you to invalidate web method caches as well as [router caches](https://dev.wix.com/docs/velo/api-reference/wix-router/ok.md). > - To invalidate the server side rendering (SSR) cache of your site, refer to the [`invalidateCache()`](https://dev.wix.com/docs/velo/api-reference/wix-site-backend/invalidate-cache.md) method in the `wix-site-backend` module. ## See also - [About Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-caching.md) - [About Router Caching](https://dev.wix.com/docs/develop-websites/articles/best-practices/caching/about-router-caching.md) - [About Routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) - [Create a Router](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) - [wix-router API](https://dev.wix.com/docs/velo/api-reference/wix-router/introduction.md) - [wix-cache-backend API](https://dev.wix.com/docs/velo/api-reference/wix-cache-backend/cache/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Best Practices for Building a Site with Velo ## Article: Best Practices for Building a Site with Velo ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-building-a-site-with-velo.md ## Article Content: # Velo: Best Practices for Building a Site with Velo Excited as you are to get started building your website using Velo by Wix, let's take a moment to plan ahead.  We've put together some best practices you should consider before you start enhancing your Wix website with the awesomeness provided with Velo. ### Coding with Velo Consider these best practices to help you code most efficiently using Velo by Wix. You will find here best practices for both beginning coders and experts, who want to know more about how Velo works.  #### Give Your Element IDs and Functions Meaningful Names Using meaningful names for element IDs and functions makes your code easier to write, read, and maintain. For example, suppose you have a button on your page that is used to submit data to a collection. Changing the button's ID from **button1** to **submit** will make it easier to find in the Autocomplete when you're assigning it an `onClick()` function. It will also be easier to understand what your code is doing when you or someone else looks at your code, possibly at a later date. ```javascript // do this $w('#submitButton').onClick(submitData); // don't do this $w('#button1').onClick(mysteryFunction); ``` #### Catch Errors in Your Code You should always strive to catch all the errors that might occur in your code and never let them propagate to the browser. When you catch errors before they make it to the browser, you can handle them gracefully. There are a few techniques you will need to use to catch the various types of errors that may occur. When working with asynchronous functions that return a Promise that may reject, you should always include a `catch()` to handle rejections. Also, before retrieving data that may not exist, you should check for existence before reading the data. For example, here we query a collection. We have added code to catch two types of errors. ```javascript wixData.query("myCollection") .find() .then( (results) => { // this "if" makes sure we don't try to read data that doesn't exist if(results.items.length > 0) { $w('myRepeater').data = results.items; $w('#message').hide(); } else { $w('#message').text = "No items found."; $w('#message').show(); } } ) // the catch handles cases where there is a system error in // retrieving the data. .catch( (err) => { $w('#message').text = "Error retrieving data."; $w('#message').show(); } ); ``` #### Format Your Code Code that is well-formatted is easier to read and maintain. Keeping proper indentation provides readers of your code, including yourself, with visual cues for understanding how the code is structured. Sometimes, when writing code, you are more focused on getting your code to work than writing it as neatly as possible. That's why we've provided the code formatting feature in the right-click menu. It removes extra lines and fixes your indentation. #### Code Only One onReady() Per Page The code you place in an [`onReady()`](https://www.wix.com/velo/reference/$w.html#onReady) callback runs when your page loads. Technically, you can call `onReady()` several times to create several callbacks that run when your page loads. However, doing so fragments your code, making it more difficult to read and maintain, without providing any obvious benefit. So keep to one `onReady()` per page. Note however, that if you have an `onReady()` defined in `masterPage.js` and one defined on a page, when that page loads, both `onReady()` callbacks will run. This is fine, because the site `onReady()` deals with elements that are shown on all your site's pages, whereas your page `onReady()` deals only with the elements on that specific page. #### Avoid side effects [Page rendering](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md#side-effects) may cause your onReady() to run twice, once on the server and once in the browser. To avoid such unwanted side effects, use the [Rendering API](https://dev.wix.com/docs/velo/apis/wix-window-frontend/rendering/introduction.md) to control what code runs on the server or the browser. #### Set Events Only Once You should only set a particular event handler for an element one time. It's important to understand that each time you set an event handler, you are adding a new handler, and not replacing the old one. So, if you set an event handler for a button using its `onClick()` function and then later attempt to "reset" the event handler by calling `onClick()` again, you have now defined two `onClick()` behaviors. Meaning, when the button is clicked, they will both run and you do not know which will run first. You should also avoid using both code and the link panel from the editor to add a link to an element. 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/11e10e4f-b84d-4136-a5a9-6109fab0b7d7/2021/04/04/6727b65c-ad3e-4b35-8377-c3588e92697b/d18ca12c-3b8d-43c2-98d8-67fc75850c1b.png)
Creating links both in the editor and code can result in a conflict that will produce unreliable functionality.  #### Try Using $w(“Type”) for Selecting Multiple Elements Did you know that there are multiple ways to select elements using the [`$w()`](https://www.wix.com/velo/reference/$w.html#$w) selector function? We all know that you can select one element at a time by providing the `$w()` function with a hashtag followed by an element ID. But you can also select elements by type by providing the `$w()` function with the type name. For example, to select all buttons on a page, you can use `$w('Button')`. The function call returns an array containing all the buttons on the page. You can then use that array to perform actions on all the buttons at once with a single function call. For example, to disable all the buttons on a page, you can use `$w('Button').disable()`. #### Use masterPage.js The masterPage.js file is located in the Page Code section of the Code sidebar (Wix Studio), or the Code sidebar (Wix Editor). Use it for elements that appear on all of your site's pages. Typically, these will be elements that you put in your site's header or footer.  Code for elements that are set to show on all pages can also be placed in masterPage.js. If you try to select a regular page element from masterPage.js, it will cause an error on all pages other than the one on which the element exists. >**Note** > Don't import functions from masterPage.js into the code files of individual pages. Doing this will cause the masterPage onReady function to run twice on that page. If there's a function that you want to use in the code for multiple pages, store it in a public .js file and import it from there. #### Declaring Variables Wisely Whenever feasible, use `const` instead of `let` to declare variables. Doing so signals your intent that the variable's value should not change. Never use `var` to declare variables. Avoid using global variables in your code. Global variables are prone to being overwritten and can hide the logic of your code. Instead of using a global variable, pass the variable as a parameter to the functions where it is needed. #### Avoid Large Blocks of Code by Using Functions Avoid writing large blocks of code. Wherever feasible, break your code down into smaller related parts and extract out to smaller functions. This makes your code easier to read and help with code reusability. #### Be Aware of Browser-Specific Code Requirements Web browsers process JavaScript code in different ways. Functionality supported by some browsers might not be supported by others. For example, on Apple iOS, the Safari and Chrome browsers do not support the regular expression **lookbehind** syntax. Many [online resources](https://caniuse.com/) allow you to quickly look up different browser requirements.  ### Security As you plan your site and consider security issues, you should review the full [Velo Security Considerations](https://support.wix.com/en/article/corvid-security-considerations#collection-permissions). The following is a summary of the issues we suggest you consider. #### Secure Private Information and Resources with the Secrets Manager You may need to add private information such as an API key to your site's code. API keys and other secrets are sensitive resources and should never be added to your page, site, and public code, since anyone can access them. Backend code is secured, but you should still follow security best practices and store your secrets separately from the code. Instead of hardcoding your secrets, you can use the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md) and the Velo [Secrets API](https://www.wix.com/velo/reference/wix-secrets-backend.html) to safely work with secrets in your code. Using the Secrets Manager also hides secrets from Site Contributors and only allows Admins to view them. #### Secure Sensitive Information in the Backend All the code in Page Code and Public code files is visible to any user who visits your site, even the code in Page Code files on a [password-protected page](https://support.wix.com/en/article/password-protecting-your-site-or-a-specific-page) or a [members-only page](https://support.wix.com/en/article/creating-members-only-pages-596999). You should therefore only put sensitive information in Backend code files and not Page Code or Public code files. The code in Backend files is not visible to site visitors but it may still be vulnerable without appropriate security validations. You should therefore implement validation mechanisms in your Backend code files where necessary. You can [learn more about your code’s visibility](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md) and how to protect it. #### Set Appropriate Permissions for Backend Web Modules [Web Module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) permissions allow you to control which users can work with functionality in your site that depends on your Web Module functions. Setting Web Module permissions allows you to ensure that no one can access or use your exported backend webModules in ways that you didn't intend. #### Set Appropriate Permissions for Database Collections You should always set the [permissions of your database](https://support.wix.com/en/article/corvid-security-considerations#collection-permissions) collections to be as restrictive as possible and to reflect your collection's common usage. Each permission should only be granted to the Admin role unless there is a specific reason to grant the permission to additional roles. Even when there is a reason to grant a permission to more roles, you should only grant it to the roles that need it. If you need to grant particular permissions that don’t fit the products permissions model, you should consider creating a specific flow of data access for the special use-case, without changing permissions for the entire collection, by using [webModules](https://support.wix.com/en/article/corvid-web-modules-calling-server-side-code-from-the-front-end) and the suppressAuth property of the WixDataOptions object, or with [data hooks](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/hooks/about-data-hooks.md). #### When to Use “suppressAuth” There may be times when you want to use the `suppressAuth` property to call [`wix-data`](https://dev.wix.com/docs/velo/apis/wix-data/introduction.md) functions on a collection without the permission checks that are set for that collection. You should only use `suppressAuth` after you run the appropriate security validations. For example, you may need to check that the visitor is a member of a specific group. #### Overriding Permission Requirements with elevate() Newer APIs use the `wix-auth` [`elevate()`](https://www.wix.com/velo/reference/wix-auth/elevate) function rather than `suppressAuth` to override permission requirements. Elevating a function allows it to be called by any site visitor. In some cases, elevating a function is required in order to use the function at all. Exercise caution and only elevate functions you are willing to let any site visitor call. #### Validate Authorized Requests for HTTP Functions You should validate your [http functions](https://support.wix.com/en/article/velo-exposing-a-site-api-with-http-functions) to ensure you are handling an authorized request. This is usually accomplished by checking for a shared value in an authorization header. ### Performance These tips will help you to take performance considerations into account when building your Velo website. #### Change Text Dynamically There are cases where you might want to add multiple overlapping text elements to your page and display each text when a specific condition is met (for example, success and error messages). But adding a lot of elements to your page can slow down your site.  Instead of using multiple text elements, you can use a single text element and change the displayed text dynamically. You can use [$w.Text.text](https://www.wix.com/corvid/reference/$w.Text.html#text) to change plain text and [$w.Text.html](https://www.wix.com/corvid/reference/$w.Text.html#html) to change styled text. As an added bonus, text is easier to manipulate using this method. #### Improve Performance in Sites with Database Collections Using data from database collections or from a 3rd-party source can be a powerful tool to enhance your site's functionality. However, sending a lot of data to the browser from the server (where your data is stored) can be a time-consuming operation that might negatively affect your site's loading time. Therefore, you want to minimize the amount of data that is sent from the server to the browser.  Whether you're using a [dataset](https://support.wix.com/en/article/about-datasets-6368396) or the [Data API](https://www.wix.com/velo/reference/wix-data.html), there are several approaches you can follow to [improve your site's performance](https://support.wix.com/en/article/velo-best-practices-for-improving-performance-in-wix-sites-with-data). #### Use async/await in onReady() Using [`async`/`await`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) functionality in your [`onReady()`](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/velo-workspace/code-editor-ide/working-in-the-code-editor.md) function delays the rendering of your page elements, decreasing the performance of your site. In many cases you'll want to avoid using `async`/`await` in `onReady()` for this reason. As an alternative, you can use the [`.then()`](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) function. On the other hand, delaying the rendering of your page elements allows database content to load in time for search bots to index your content, which is [important for SEO](https://dev.wix.com/docs/develop-websites/articles/best-practices/seo-best-practices.md#inside-the-onready-function). You'll need to decide what's best for your site on a case-by-case basis. For more information on handling promises with `async`/`await` or `.then`, see [Working with Promises](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md). #### Avoid Code that Could Result in Repeater Performance Limitations If you’re working with dynamic repeater content that is updated fairly often, and especially if you are filtering or sorting your repeater data, you may not want to bind event handlers to repeater item elements from within the onItemReady() function. Although it is convenient to do so using the scoped $item selector, this practice may cause several event handlers to be set for the same item, as well as add multiple copies of the callback function to the event handler, affecting the performance of your site. Alternatively you can combine the use of some of the following APIs to bind your repeater items: * $w Element API [`at()`](https://www.wix.com/velo/reference/$w/at) * Repeater APIs [`forItems()`](https://www.wix.com/velo/reference/$w/repeater/foritems) and [`forEachItem()`](https://www.wix.com/corvid/reference/$w/repeater/foreachitem) * Dataset APIs [`getCurrentItem()`](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitem) and [`getCurrentItemIndex()`](https://www.wix.com/velo/reference/wix-dataset/dataset/getcurrentitemindex) #### Be Mindful of Data and Backend Quotas Wix places quotas on data and backend code requests on your site. Data requests include using the [Content Management System (CMS)](https://support.wix.com/en/article/about-the-content-manager-7160473), [Datasets](https://support.wix.com/en/article/about-datasets-6368396), [Wix Forms](https://support.wix.com/en/article/wix-forms-an-overview), and Velo’s [Wix Data API](https://www.wix.com/velo/reference/wix-data). Backend requests include calls to [web modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) from your frontend code, using [routers](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/create-a-router.md) hooks, and calling [HTTP functions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/integrations/exposing-services/about-custom-site-apis.md). You can take steps to make sure your code isn't exceeding the quotas. Learn more about working with [data](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/data-features.md) and [backend](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/limits-and-optimization/compute-features.md) quotas. ### Database Collections Take a few minutes to review these best practices for working with database collections and configuring datasets. #### Validate User Input Before Storing It in a Collection Collections are important data stores. To make sure that the data in your collection is valid, complete, and consistent, validate the input data before storing it in a collection. Validate your input data at the field level using the input element’s settings: * Data type * Maximum and minimum values * Maximum character length * Regex validation patterns For more complex validations use the `onCustomValidation()` event handler. Both the field setting and custom validations are triggered by the `valid` property. To validate entire records, use the wix-data hooks [`beforeInsert`](https://dev.wix.com/docs/velo/apis/wix-data/hooks/before-insert.md), [`beforeUpdate()`](https://dev.wix.com/docs/velo/apis/wix-data/hooks/before-update.md) and [`beforeRemove`](https://dev.wix.com/docs/velo/apis/wix-data/hooks/before-remove.md). Hooks give you the ability to control the data going into your collection with backend code, processed before it gets to your collection. Hooks are independent of the front end and will run regardless of what triggers the write – frontend code, the content manager, or importing data (Sandbox only). When using a dataset, use the [`onItemValuesChange`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/on-item-values-changed.md), and [`onBeforeSave`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/on-before-save.md) to validate data before the [`save()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/save.md) is executed. See [About Validating User Input with Code](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/user-input/about-validating-user-input-with-code.md) for more information. #### Datasets: Read or Write? (Be Careful With Both!) Datasets can be configured as read, write, or read-write. Read-write datasets should only be used where both read and write or update are required. When using a read-write dataset, take care to understand all of the potential flows that may exist. Let’s take a common use case where we have a collection with existing data, a table to display that data, and a set of input elements below the table to allow the visitor to edit the selected row. We have buttons for submit, and delete, connected to the submit, and remove functions in the dataset. We also have a new button connected to the \`New\` function in the dataset which will create a new blank item, ready for editing in the input elements. The table, the input elements and the buttons, are all connected to a read-write dataset. As the visitor clicks on each row in the table, the values of the input elements are updated with the values of the selected row. The visitor can now edit the contents of a row and click submit to save the data.
The following flows can produce some unexpected outcomes: * Be careful when using lists and repeaters with read-write datasets. From our common use case above, if a row was selected, and the values changed in the input elements, selecting another row will cause the changed data to be saved without clicking **Submit**. This happens because any change to the dataset’s index will save the changed data to your collection. The index can be changed by selecting a different item in a connected list or by using the [`setCurrentItemIndex()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/set-current-item-index.md) function. * Pages should only allow the selection and editing of a single item. Use the [`revert()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/revert.md) function or a button connected to the `Revert` dataset action, if you want your visitors to be able to undo changes. * Avoid overwriting existing data when trying to create new items. Use the [`new()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/new.md) function or a button connected to the `New` dataset action. * In our common use case, site visitors may try to delete an item by deleting the contents of each input element, and clicking **Submit**. This will update the selected item setting each field to blank but will not delete the item. Use the [`remove()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/remove.md) function or a button connected to the `Delete` dataset action for any delete operations. Make sure that your site visitor can clearly identify which item is to be deleted. * A read-write dataset reads your data when the page is ready, so if you are building a form to update a collection, the read-write dataset displays the first item in the collection in your input fields. * From a performance point of view, when your page loads, a read-write dataset will take more time as it has to retrieve the data from the collection. Speed up page loading by implementing the minimum functionality required. **When to Use Datasets and When to Use Code** First of all, we recommend that you _don’t use both together_! For simple applications with little potential for change in the structure of your collections, datasets provide a fast, safe, and managed solution. Datasets are very easy to implement with no coding needed, and provide a quick and easy interface to your collections. If your application is complex, with processing required before displaying or saving your data, coding the interface using the `wix-data` API is a better choice. Coding is more flexible and easier to extend and modify than datasets. When using code, you control the entire interaction with the collection. `wix-data` provides functions, callbacks, and hooks giving you the option to run procedures and validate your data at critical points in your process. These points include before and after read, create, update, and delete operations. Coding also gives you more complex query filters and the ability to use aggregations for reporting applications. In general code runs faster than a dataset, especially if you have many datasets on the same page. However, dynamic datasets can be faster due to the way they request the data. Also bear in mind that you can use caching with a dataset but not with code.  For better code performance, you can run the code on the backend, lightening the load on the browser. If you do decide to mix code and datasets, _be careful_. Multiple processes acting on the same data can have unpredictable outcomes and lead to corruption and inconsistencies in your collections. No one wants that. ### Design The best practices, design tips, and editor tools listed here can save you time and let you focus more on your coding with Velo. #### Use SVG Images SVG is a vector graphic format based on XML that is flexible and scalable without sacrificing quality. You can easily change the image’s content, color, and shape dynamically, as opposed to when working with other graphic formats. You should use [SVG files](https://www.wix.com/velo/reference/$w.VectorImage.html) instead of other image files when you need this kind of flexibility. #### Check out Other Panels for Helpful Features Other panels in the editor contain functionalities that make your coding experience easier. For example, your page might contain many elements located closely together. It can be hard to find the element’s ID that you want to reference in your code. You can use the [Layers panel in Wix Studio](https://support.wix.com/en/article/studio-editor-using-the-layers-panel), or the [Layers panel in Wix Editor](https://support.wix.com/en/article/using-the-layers-panel) to select the element from a list and position yourself exactly on it, so you can see the element’s properties.   You can use the [Inspector Panel](https://support.wix.com/en/article/studio-editor-using-the-inspector-panel) (Wix Studio), or the [Editor Toolbar](https://support.wix.com/en/article/wix-editor-using-the-editor-toolbar-to-adjust-elements) (Wix Editor) in conjunction with Velo to quickly and easily get your design just right. For example, you can use these tools to align, position, and resize your elements. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Security Best Practices ## Article: Security Best Practices ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md ## Article Content: # Security Best Practices In general, a site is secure without having to do anything. Wix takes care of that. However, there are certain situations where you have to take some precautions so that you don't expose sensitive data to a site's visitors. ## Collection permissions If you are using custom collections you should always set the permissions of your database collections to be as restrictive as possible. If the collection contains sensitive data such as PII or secrets, the **Read** permissions should be granted for the **Admin** role only, unless there is a reason to grant a specific permission for additional roles. Even when there is a reason to grant a permission for more roles, you should only grant it for the roles that need it. Also, to prevent anyone from removing or updating the data, you should set the **Update** and **Remove** permissions to the **Admin** role. Or, if there is a UX flow for users to update or remove the data, then you can set those permissions to **Site member author**. Here are some examples: |Example |Recommended permission settings | |---|---| |**Scenario: Form submission** An input form that you want anyone to be able to use. **Solution** On the collection that the form is connected to, set the **create** permissions to the **Anyone** role. Keep all the other permissions restricted to the **Admin** role.|Read: Admin
Create: Anyone
Update: Admin
Delete: Admin |**Scenario: Site content** A page that displays content from a collection to anyone. **Solution** Set the **read** permission of that collection to the **Anyone** role. Keep all the other permissions restricted to the **Admin** role.|Read: Anyone
Create: Admin
Update: Admin
Delete: Admin |**Scenario: Member-generated content** A member-generated comments section where members can post comments that anyone can see, but only the poster can update or delete the comment. **Solution** Set each permission based on who needs to access it.|Read: Anyone
Create: Site member
Update: Site member author
Delete: Site member author ## Unused permissions Be careful when granting a permission to a collection even if you don't expose that collection’s functionality in your site. Here are some examples: * **Unused create permission** If your site doesn’t contain a form for site visitors to create content for a specific collection, you may think you can safely set the **create** permission for that collection to **Anyone**. That is not the case. A malicious site visitor can inject data into your collection without a form. Make sure your collection is protected by restricting the **create** permission to the **Admin** role. * **Unused read permission** If you have a collection for internal use that you don't use on any of your site's pages, you may think you can safely set the **read** permission for that collection to **Anyone**. That is not the case. A malicious site visitor can still read the data from this collection. Make sure your collection is protected by restricting the **read** permission to the **Admin** role. ## Temporarily bypass collection permissions Sometimes, you may need to grant access to collection data only in a specific situation or only to a specific user. You may need to expose some of the data in a collection, while keeping the rest private. In these cases, extending the permissions of the collection to more users is a security risk. Doing this exposes all the collection data to users with the permitted roles all the time. Instead, set the collection permissions as appropriate for most situations. Then, when you need to grant access to the collection to a specific user or for a specific use case, perform that operation in backend code. You can grant temporary access to collections using the `suppressAuth` parameter that’s available as an option for many [Wix Data](https://www.wix.com/velo/reference/wix-data) functions. Setting `suppressAuth` to true allows a data request in backend code to interact with a collection even if the site visitor requesting the data doesn’t have permission to access that collection. To grant temporary access to a collection, define a function in your backend code that makes a data request using `suppressAuth`. Export this function for use in your frontend code. See [About Collection Permissions](https://support.wix.com/en/article/collection-permissions-an-overview) for more information. >**Note** > * A backend function that suppresses Wix Data's permissions checks must do at least one of the following: >    (a) Perform its own checks before accessing a collection. >    (b) Filter out sensitive collection data before passing anything back to frontend. > Anyone can call exported backend code, as described [below](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md). This code therefore presents a serious security risk.  > * Only suppress permission checks when absolutely necessary. Leave Wix Data’s permissions checks enabled as much as possible. This way, even backend operations run only for users who have been granted permission for that operation. Here are 2 examples of using `suppressAuth` to grant specific and temporary access to collection data: * **Access for site members with specific roles** Wix allows you to create custom roles for site members. However, you currently can’t set collection permissions by role. To allow only members with a certain role to access a collection, set the collection’s permissions to Admin. Then, implement backend code that suppresses permission checks for site members with the desired role.
Show me how This code defines two functions: * **isPermitted()** : Uses the [Wix Members API](https://dev.wix.com/docs/velo/api-reference/wix-members-backend/introduction.md) to check if a site member has a Staff role * **queryCollection()** : Uses suppressAuth to return a collection's data if isPermitted() returns true. ```javascript import { Permissions, webMethod } from 'wix-web-module'; import { currentMember } from 'wix-members-backend'; import wixData from 'wix-data'; const isPermitted = webMethod(Permissions.Anyone, async () => { try { let hasPermittedRole = currentMember.getRoles() .then((roles) => { const staffRoleCheck = roles.filter(obj => obj.title === 'Staff'); if (staffRoleCheck.length > 0) { return true; } else { return false; } }) return hasPermittedRole; } catch (error) { console.error(error); return false; } }); ``` ```javascript export const queryCollection = webMethod(Permissions.Anyone, async () => { const accessGranted = await isPermitted(); if (accessGranted) { try { let collectionData = wixData.query('myCollection') .find({ suppressAuth: true }); return collectionData; } catch (error) { console.log(error); } } else { return "Access denied"; } }); ```
* **Access to specific collection fields** Some collections contain both private and public data. In this case, you may want to expose the public data to site visitors while keeping the private data secure. To do this, set the collection’s permissions to Admin. Then, implement backend code that suppresses permission checks and returns only the public data.
Show me how This code uses `suppressAuth` to query a collection and returns only the '\_id' and 'comment' fields. ```javascript import wixData from 'wix-data'; export function getData(){ return wixData.query("myCollection") .find({"suppressAuth": true}) .then((results) => { if (results.totalCount > 0) { const filteredResults = results.items.map( (item) => { return {"\_id" : item.\_id, "comment" : item.comment} }) return filteredResults } }) .catch((error) => { console.log("Error:", error.message); }); } ```
## Storing personal information in a collection You can store a site visitor’s personal information in a collection, provided that you set the collection permissions so that only the **Site member author** can **read**, **update**, or **delete** content. You may also want to store visitor information using the built-in [Contact List](https://support.wix.com/en/article/about-your-contact-list). This allows you to use this information with all the other contact list functionality that Wix provides. You can then also use Velo APIs to perform operations that involve site members or contact data. The following APIs are available: * [wix-crm.v2](http://www.wix.com/code/reference/wix-crm-v2.html) * [wix-crm-frontend](http://www.wix.com/code/reference/wix-crm.html) * [wix-crm-backend](http://www.wix.com/code/reference/wix-crm-backend.html) * [wix-members.v2](https://www.wix.com/velo/reference/wix-members-v2) * [wix-members-frontend](https://www.wix.com/velo/reference/wix-members) * [wix-members-backend](https://www.wix.com/velo/reference/wix-members-backend) ## Signup forms When you [set up a member signup form](https://support.wix.com/en/article/site-members-editing-the-signup-settings-for-the-default-and-custom-forms#customizing-your-default-member-signup-form) for your site, you have 2 options for limiting who is allowed to sign up: * **Everyone**: When a new member signs up, they are approved automatically. You do not need to do anything. * **People I approve**: When a new member signs up, you receive a notification, both by email and in your site's dashboard, asking if you want to approve or reject them. Only those who you approve become site members. If your site members have permissions for any of the collections on your site, then enabling automatic approval of new members sign up is the same as setting those permissions to collections to **Anyone**. This is because anyone can now become a site member and use the site member permissions. This is a potential security concern. You should think carefully about whether site members have access to any sensitive collection information before allowing anyone to become a member. ## Code visibility All page and public code is visible to any site visitor. Note that page code on a [password protected page](https://support.wix.com/en/article/wix-editor-password-protecting-a-specific-page-of-your-site) or [members only page](https://support.wix.com/en/article/studio-editor-using-the-members-area#showing-exclusive-member-only-content) can be viewed by any site visitor, including those who don't have the password and are not members. Therefore, don't expose sensitive information in page or public code, including in the `masterpage.js` file. Backend code is not visible to site visitors. It's safe to use sensitive information there and export the results to frontend code. However, even though malicious visitors can't see what exported backend functions do, they can still call those functions if they know their names.  They can do this using any arguments they want, and examine any return values. Therefore, any exported backend code should [perform validations](https://dev.wix.com/docs/develop-websites/articles/best-practices/security-best-practices.md#validations-and-checks) before carrying out potentially harmful operations or returning sensitive information. Also, backend functions that are not called from public code should not be exported from .web.js or .jsw files.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2018/06/17/41dc888c-dbe4-4ff2-ad41-67e7b9e5c625.png)
## API keys If you access a 3rd party service that requires an API key or other sensitive information, you should always store that information in the [Secrets Manager](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/secrets-manager/about-the-secrets-manager.md). Never use API keys in page, public, or `masterpage.js` code. Instead, export a function from a [backend web module](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md) that uses the [Secrets API](https://dev.wix.com/docs/velo/api-reference/wix-secrets-backend/introduction.md) to extract the key and [calls the 3rd party service](https://dev.wix.com/docs/velo/apis/wix-fetch/introduction.md). Call that function from your page, public, or `masterpage.js` code. ## Signup and login forms If your site has a [Member's Area](https://support.wix.com/en/article/site-members-about-the-members-area), you can add forms using signup and login pages. One type of form you can add lets you use code to [customize member signup and login](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/creating-a-custom-registration-form-with-code.md). Because signing up and logging in involve the transfer of sensitive information, it’s best to [prevent signup & login API calls from running in the frontend](https://support.wix.com/en/article/velo-backend-signup-and-login#security-implications) when using a form. ## Validations and checks If you want to perform a security check or validation in your code, you should always do so using backend code. Any check or validation in your page, public, or `masterpage.js` code can be easily read, manipulated, and circumvented by a malicious site visitor. For example, consider the following Page code intended to reveal a secret key to a specific site visitor: ```javascript import { currentMember } from 'wix-members-frontend'; $w.onReady( () => { $w('#validateButton').onClick( () => { currentMember.getMember() .then((member) => { if(member.loginEmail === 'secretemail@mail.com') { // show secret key $w('#secretText').text = '43ne5gfou94tfe'; } else { // show denial message $w('#secretText').text = 'Access denied!'; } }) }) }); ``` There are two major problems with this code: 1. The security check is revealed to all site visitors. Anyone can see that the correct email address is `secretEmail@mail.com`. 2. Sensitive information is revealed to all site visitors. Anyone can see that the secret code is `43ne5gfou94tfe`.  The correct way to do this is to move both the security check and the secret code to a backend web module as follows: ```javascript // In backend file: secureModule.web.js import { Permissions, webMethod } from 'wix-web-module'; import { currentMember } from 'wix-members-backend'; import { getSecret } from 'wix-secrets-backend'; export const secureCheck = webMethod(Permissions.Anyone, async () => { try { const member = await currentMember.getMember({ fieldsets: ['FULL'] }); const memberEmail = member.loginEmail; const secretEmail = await getSecret("secretEmail"); // "secretemail@mail.com" if (memberEmail === secretEmail) { const secretValue = await getSecret("secretValue"); // "43ne5gfou94tfe" return secretValue; } else { return "Access denied!"; } } catch (err) { console.error(err); return "An error occurred"; } }); ``` Although malicious site visitors can call the backend function, they can't gain any information by doing so since the function doesn’t reveal any sensitive information. You can call this function safely from page code as follows: ```javascript import { secureCheck } from 'backend/secureModule.web'; $w.onReady(() => { $w('#secretButton').onClick(() => { secureCheck() .then ((message) => { $w('#secretText').text = message; }); }) }); ``` This page code is visible to any site visitor. However, since all the security check logic was moved to backend code, seeing the code doesn't reveal any sensitive information to malicious site visitors. >**Note** > You can also [set permissions](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) that define who can call each individual exported function in a web module. ## See also + [About Web Modules](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#permissions) + [About Collection Permissions](https://support.wix.com/en/article/cms-collection-permissions-overview) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: SEO Best Practices ## Article: SEO Best Practices ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/seo-best-practices.md ## Article Content: # SEO Best Practices Here are some tips for improving a site's visibility in search engines and avoiding SEO pitfalls. ## Make sure search engines can crawl your content One of the first steps to ensure effective SEO is making sure search engines can crawl and index your site content. Some of your site content may be dynamic. How can you make sure that search bots can view your content? ### Search bots like HTML Wix sites are built using editor components that are rendered into HTML. You can also customize the content and layout of your site by adding code using [Velo APIs](https://www.wix.com/velo/reference/). Search bots access your page content by sending an HTTP request to your page and reading the HTML content generated from the initial page rendering. At a later date, some search bots also render the page themselves and run any JavaScript code. Since not all search bots run JavaScript code, you should make sure the code returning the content you want to be indexed runs before your page is initially rendered. Content not included in the initial HTML output may be indexed after a long delay, indexed incorrectly, or not at all. For this reason, and also to increase the performance of your site, Wix uses server-side rendering (SSR) to render your page's components on the server and return them to the browser as HTML. How can you make sure your content is included in the SSR version of your page? ### Inside the onReady() function Anything that occurs in the [`onReady()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md) function is rendered on the server, returned as HTML, and seen by search engines. > **Note:** As the onReady() may be called twice during the rendering process, once on the server and once in the browser, be aware to avoid any unwanted [side effects](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/page-rendering/about-page-rendering.md#side-effects). For most use cases, it's enough to just call the function in `onReady()`. For asynchronous functions and functions that run following a delay, the `onReady()` promise may resolve before the function's promise has a chance to resolve. In such cases, if you need the returned content to be indexed, you can block the `onReady()` from resolving until your function's promise resolves. You can indicate to the server that it should wait by returning the promise in `onReady()`. For example, let's say you have a repeater on your page that is populated with data queried from a database collection: ```javascript import wixData from 'wix-data'; $w.onReady(function () { $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#text").text = itemData.title; $item("#image").src = itemData.image; }); wixData.query("MyCollection") .find() .then((results) => { $w('#myRepeater').data = results.items; }); }); ``` In this example, the asynchronous query's promise may not resolve before the `onReady()` promise resolves, and the SSR version of your page may not include the queried repeater content. To ensure that the query resolves before `onReady()`, we can return the query in `onReady()`. See the change to Line 9: ```javascript import wixData from 'wix-data'; $w.onReady(function () { $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#text").text = itemData.title; $item("#image").src = itemData.image; }); return wixData.query("MyCollection") .find() .then((results) => { $w('#myRepeater').data = results.items; }); }); ``` Note that returning a promise in the `onReady()` function may affect the [performance](https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-improving-performance-in-wix-sites-with-data.md) of your site, as all the data will load before the page is rendered. Since there is a tradeoff between SEO and performance, you might want to wait for the main content that is important for indexing, and let auxiliary content such as the time or weather information load later. >**Note:** > Database content that is loaded into page elements using a dataset (instead of code) is included in the SSR version of your page and will be seen by search engines. ### Test your site You can emulate a search bot to test your site's content by setting your browser's user agent to a specific bot. For example, you can emulate a Google bot and check which site content Google bots will initially crawl.
Show me how To emulate a Google bot in the Google Chrome browser: 1. Navigate to your published site. 2. Open [Developer Tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools).  3. Click the 3-dots menu at the top right. 4. Select **More tools** \> **Network conditions**.
5. In the User Agent section, clear the **Select automatically** checkbox. 6. Select **Googlebot**.
7. Reload your site. Now you're viewing the page content Google bots will initially crawl and index.
## Links and SEO Search engines learn a lot about your site from internal site links. Since internal links impact your site's SEO, it's important to know how to set up and manage links on your site. ### Crawlable links Google bots only crawl links with an HTML `` tag with an `href` attribute. There are several ways to add crawlable `href` links to your Wix site: * Add a site menu to your site ([Wix Studio](https://support.wix.com/en/article/studio-editor-adding-and-managing-menus) | [Wix Editor](https://support.wix.com/en/article/adding-a-site-menu)). * Add a hyperlink to your text ([Wix Studio](https://support.wix.com/en/article/studio-editor-adding-and-customizing-text) | [Wix Editor](https://support.wix.com/en/article/wix-editor-adding-a-link-to-text)). * Use the link button to add a link to a clickable element such as a button or image. ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/05d83366dac5136f17daafad012de121.png) * Use a dataset to [connect URLs](https://support.wix.com/en/article/connecting-a-button-to-a-database-collection) from a database collection to clickable elements such as a buttons or images. Other methods of adding links to your site are not guaranteed to generate `href` links. You can add non-crawlable links to your site, but you should make sure that every page of your site is reachable from at least 1 crawlable link. ### Use page query parameters for pagination There are different ways to handle loading more page content when the content is too long to fit on a single page. One method is to use a numbered menu that loads a specific number of items for each numbered button that is clicked. If you are using a numbered menu for pagination and you want search engines to crawl and index each batch of content as an independent page, add a page query parameter to the link for each numbered button: `?page=`. Google views URLs with numbered page query parameters as links to independent pages. For example, let's say you have a numbered menu with several buttons:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/03/31/d1941aac-eb17-41a5-92a9-1d3bddafa4cc/bfef9323-9fa2-425c-a3e6-706575374078.png)
Google indexes the following as 2 separate pages: * `#button2` URL: `https://www.example.com?page=2` * `#button3` URL: `https://www.example.com?page=3` >**Note:** > For the link to the first page, don't set the query param to **?page=1** since you'll have 2 URLs pointing to the same page. Instead you can link to the main page. ### Does URL case matter? Google treats URLs as case sensitive. For example, Google views each of these URLs as unique: * `www.mysite.com/page.html` * `www.mysite.com/PAGE.html` Google recommends exposing either the uppercase or lowercase version of a URL, and not both. Even if you've set both URLs to point to the same page so that a site visitor reaches the same destination regardless, you may not want both versions out there on the web pointing to your page.   When multiple URLs point to the same page, Google chooses only one to represent the content on your page, and highlights only that URL in search results. And it might not be the URL you prefer. So which case should you choose, uppercase or lowercase? Lowercase is recommended, since it's more common and you can assume that at least some people will use the lowercase URL to link to your site. ## Images and SEO Site images play a significant role in determining your site's SEO. But how do search engines "read" your images? They check the image's alternative text (alt text), a short but meaningful description you can assign to your image. You can [add alt text](https://support.wix.com/en/article/studio-editor-adding-alt-text-to-images) to an image using the editor, but if you are displaying multiple images from a database collection, you can dynamically assign alternate text to your images with code using the [`alt`](https://dev.wix.com/docs/velo/velo-only-apis/$w/image/alt.md) image property, or by connecting to alternative text stored in a database collection using a dataset.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/04/05/32475c27-3da7-484f-bcca-6b3dc2bbba1f/24c74a65-a35d-4276-86b3-5f828c5ff24b.png)
## Dynamic pages and routers [Dynamic pages](https://support.wix.com/en/article/cms-about-dynamic-pages) and [router pages](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/about-routers.md) are different than regular pages in that they contain dynamic data. SEO information for a dynamic or router page must be set dynamically, so that it reflects the real content the pages will hold when they are viewed. You can [define SEO information](https://support.wix.com/en/article/working-with-seo-settings-for-dynamic-pages) for your dynamic pages in the editor. You can dynamically [define the following SEO information](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md) for your router pages: * [Meta tags](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md#step-1--set-meta-tags-for-router-pages): Help search engines learn about your pages. * [Sitemap](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/routers/add-seo-to-your-router.md#step-2--create-the-sitemap-for-your-router): Help search engines find your pages. ## Build a great site structure Site structure refers to how you organize content on your site. For example, how do pages with your main content link to pages with subtopics? How many pages will a site visitor have to navigate to reach the content they want? Is the connection between pages with related content organized or random? Planning and mapping out an organized site structure will make it easier for search engines to crawl and index your site. Google awards [sitelinks](https://developers.google.com/search/docs/appearance/sitelinks?hl=en&visit_id=638694267737097538-2339297366&rd=1), additional links that appear beneath the main URL in a Google search, to sites with great site structures. Use [database collections](https://support.wix.com/en/article/creating-a-database-collection) and [dynamic pages](https://support.wix.com/en/article/creating-unlimited-site-pages-with-a-dynamic-page) to improve and organize your site structure. Consider the following factors when planning your site: * **Organized hierarchy:** Make sure the connection between pages on your site isn't random. Your site hierarchy should be organized and fairly shallow (not too many levels).  This is easy to accomplish using dynamic pages, where you can automatically generate multiple pages with similar structure and different content all relating to a main index page.  * **URL structure:** The structure of your URLs should mirror your navigation hierarchy. Each level of your site should be represented by a section of your URL containing an appropriate keyword. For example, if you've got a recipe site divided by cuisine type and then by course, a site visitor who selects Italian cuisine and then appetizer recipes should see a URL that reflects the site hierarchy: ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/0ca9e8ba-16ed-4cd8-a1e1-58d01c4420f7/2020/03/30/4eb3ef8a-8bbc-4640-8a38-30284c49662b/7269c6e8-05e5-4f2b-ac79-e0257fa455ca.png) You can automatically generate [structured URLs](https://support.wix.com/en/article/customizing-your-pages-url-structure-in-seo-settings) for your pages using dynamic list and item pages. * **Internal links:** Search engines use internal links to crawl the pages of your site. In general, the more internal links the better, but minimally every page on your website should have a link to and from another page on your site. You can create an index page or a dynamic list page to easily link to and from dynamic item pages on your site. >**Note:** > [SEO settings](https://support.wix.com/en/article/working-with-seo-settings-for-dynamic-pages) for dynamic pages are different than SEO settings for regular site pages. ## Set SEO data with the SEO API You can use the [SEO API](https://dev.wix.com/docs/velo/apis/wix-seo-frontend/introduction.md) to get and set SEO information for your site pages: * **Title tag:** Gets and sets the clickable heading that appears in search result pages. * **Link tags:** Gets and sets link relation attributes for multiple URLs that point to your page. For example, `rel canonical` lets search engines know which URL represents the master copy of your page. * **Meta tags:** Gets and sets metadata, additional information about your page. For example, you could use meta tags to control what content appears when your page is shared on Facebook, or to prevent search engines from indexing your page. * **Structured data markup:** Gets and sets additional information about your page in a standardized format that can be read by search engines. Search engines use this information to classify your page content and possibly display the search result for your page as [rich results](https://yoast.com/what-are-rich-snippets/) (snippets). For example, you can use structured data to let search engines know your page content is a recipe or contact information for an organization. >**Note:** > To ensure the SEO data you set will be read by search engines, make sure to set such data in the [`onReady()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md) event handler. ## Track site events You can connect a 3rd-party [marketing tool](https://support.wix.com/en/article/about-marketing-integrations) such as Google Analytics or Facebook Pixel to your Wix site and collect valuable information about your visitors' behavior. Wix helps you track specific visitor events such as page views and purchases and report them to your integrated marketing tool. You can then use this information to optimize your site. If you want to track additional visitor actions not included in the default tracking events available using Wix marketing integrations, you can use the [Track Event API](https://dev.wix.com/docs/velo/apis/wix-window-frontend/track-event.md). For example, you can [add tracking code](https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-editor-elements/sending-tracking-and-analytics-events.md) to a download button and report to a 3rd-party tool every time a visitor downloads a document.  ## See also * [Wix SEO Setup Checklist](https://support.wix.com/en/article/improving-your-online-presence-with-the-wix-seo-setup-checklist): A step-by-step plan designed to help you improve your site's SEO.  * [Wix SEO Guide](https://www.wix.com/seo/learn/wix-seo-guide): Everything you need to know to set up your website's SEO and get found online. * [Google SEO Guidelines](https://developers.google.com/search/docs/guides/get-started): Don't forget to carefully check Google's SEO guidelines. If you are experiencing SEO issues with your site, it's worthwhile to review Google's guidelines again and make sure you aren't inadvertently missing any important details. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Best Practices for Improving Performance in Wix Sites with Data ## Article: Best Practices for Improving Performance in Wix Sites with Data ## Article Link: https://dev.wix.com/docs/develop-websites/articles/best-practices/best-practices-for-improving-performance-in-wix-sites-with-data.md ## Article Content: # Velo: Best Practices for Improving Performance in Wix Sites with Data Using data from database collections or from a 3rd-party source can be a powerful tool to enhance your site's functionality. However, sending a lot of data to the browser from the server can be a time-consuming operation and negatively affect your site's loading time. Therefore, you want to minimize the amount of data that is sent from the server to the browser. This article lists a number of approaches you can use, whether you're using a dataset or the Data API, to improve your site's performance. ### Download Only the Items You Need When retrieving data on a page, you should only retrieve the items that you need. If you need to display a large number of items, you should consider only retrieving some of the items at first and then more items when needed. For example, suppose you want to display items from a collection in a repeater. Instead of showing all the items from your collection when the page loads, you can start by showing only some of the items. If necessary, you can add a way to load more items or to navigate through pages of items.
#### Elements Populated Using a Dataset Limiting the amount of data you retrieve when using datasets is easy. First, set the number of items to show in your repeater using the **Number of items to display** setting in your [dataset's settings](https://support.wix.com/en/article/adding-and-setting-up-a-dataset#step-2-setting-up-your-dataset-1). This not only limits the number of items displayed but also limits the number of items retrieved by the dataset at one time. The number of items retrieved at one time is also known as the dataset's page size. If applicable, you should also [set a filter on the dataset](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) so it only retrieves the items you need. Next, you can optionally set up a way for site visitors to load more items or navigate through pages of items. This allows you to speed up the initial loading time of your page while still eventually displaying all items your site visitor wants to see. To do so, add a button or buttons to your page and set their click actions to the **Load More**, **Previous Page**, or **Next Page** actions.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/09/09/63ffe159-7950-4ac0-b0a6-fa39947cbdbc/a48e0137-7d0c-4f69-884c-4c74f86fbc7c.png)
**Learn more:** - Learn how to connect a button [here](https://support.wix.com/en/article/connecting-a-button-to-a-database-collection). - Learn more about dataset actions [here](https://support.wix.com/en/article/working-with-dataset-actions-for-buttons-and-images).
#### Elements Populated Using Code You can also limit the amount of data you retrieve when using code to populate elements. Start by using [filtering functions](https://www.wix.com/velo/reference/wix-data.WixDataFilter.html), such as `eq()` and `gt()`, to only query for relevant items. Then you can add the [`limit`](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#limit) function to your query chain to only retrieve some of the relevant items at first. The limit you set also defines how many items are retrieved in each page of query results. When you need more items later, you can use the various [paging functions of the query result](https://www.wix.com/velo/reference/wix-data.WixDataQueryResult.html), such as `hasNext()` and `next()`, to retrieve additional pages of items. For example, here we populate a repeater with data from a query that only retrieves "active" items. We begin by only retrieving the first six items and show a "load more" button if there are additional items to show. When there are no more items to show, the "load more" button is collapsed. ```javascript import wixData from "wix-data"; let queryResults; $w.onReady(async function () { queryResults = await wixData .query("myCollection") .ascending("title") .eq("status", "active") .limit(6) .find(); $w("#myRepeater").data = queryResults.items; if (queryResults.hasNext()) { // load more button was set as collapsed on load $w("#loadMore").expand(); } }); export async function loadMore_click(event) { queryResults = await queryResults.next(); let data = $w("#myRepeater").data; $w("#myRepeater").data = data.concat(queryResults.items); if (!queryResults.hasNext()) { $w("#loadMore").collapse(); } } export function myRepeater_itemReady($item, itemData, index) { // set up repeater items as they are created $item("#titleText").text = itemData.title; $item("#container").background.src = itemData.pic; } ``` ### Delay Loading Data Another approach that can be used when you have a lot of data you want to display is delayed loading. When using delayed loading, you first download a small number of items, which will load quickly, and present those items to site visitors. You can then download the rest of the data at a later time. For example, suppose you want to display items from a collection in a repeater. Instead of showing all the items from your collection when the page loads, you can start by showing only the items site visitors see at first. Typically, you want to download enough data to populate what a site visitor sees when the page loads (known as "above the fold"). Additional items, those that a visitor would have to scroll to see (known as "below the fold"), you can download in the background after the page has loaded.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2020/02/09/677b415d-cbfa-4478-bf36-9f0837da06e8/bb26a9be-59c8-45f8-8205-1df3d950bb8e.png)
#### Elements Populated Using a Dataset Delayed loading of data when using datasets requires you to change the settings of the dataset and to add a little bit of code to your page. First, set the number of items to show in your repeater using the **Number of items to display** setting in your [dataset's settings](https://support.wix.com/en/article/adding-and-setting-up-a-dataset#step-2-setting-up-your-dataset-1). Set it to the number of items that are shown above the fold. This not only limits the number of items displayed but also limits the number of items retrieved by the dataset at one time. The number of items retrieved at one time is also known as the dataset's page size. If applicable, you should also [set a filter on the dataset](https://dev.wix.com/docs/develop-websites/articles/databases/wix-data/displaying-data/display-database-collection-content-in-a-repeater.md) so it only retrieves the items you need. Next, add code to your page that incrementally downloads the rest of the items from your collection one page at a time and displays them in the repeater. This code sets an event handler that runs when the dataset has loaded its first set of data. The event handler checks whether there are any additional pages of data to download. If there is, it downloads a page of data and then checks again, until there are no more pages left. ```javascript $w.onReady(function () { $w("#myDS").onReady(async () => { while ( $w("#myDS").getCurrentPageIndex() < $w("#myDS").getTotalPageCount() ) { await $w("#myDS").loadMore(); } }); }); ``` #### Elements Populated Using Code Start by using [filtering functions](https://www.wix.com/velo/reference/wix-data.WixDataFilter.html), such as `eq()` and `gt()`, to only query for relevant items. Then you can add the [`limit`](http://wix.to/94BuAAs/wix-data.WixDataQuery.html#limit) function to your query chain to only retrieve some of the relevant items at first. The limit you set also defines how many items are retrieved in each page of query results. Limit your query to the number of items that are shown above the fold. Once those items are loaded, you can use the various [paging functions of the query result](https://www.wix.com/velo/reference/wix-data.WixDataQueryResult.html), such as `hasNext()` and `next()`, to retrieve additional pages of items. For example, here we populate a repeater with data from a query that only retrieves "active" items. We begin by only retrieving the first six items, which take up all the space above the fold on our site. After those items are loaded, we query the remaining items and add them to the repeater as they are retrieved. ```javascript import wixData from "wix-data"; let queryResults; $w.onReady(async function () { queryResults = await wixData.query("Test").ascending("title").limit(6).find(); $w("#myRepeater").data = queryResults.items; loadRemaining(); }); async function loadRemaining() { while (queryResults.hasNext()) { queryResults = await queryResults.next(); let data = $w("#myRepeater").data; $w("#myRepeater").data = data.concat(queryResults.items); } } export function myRepeater_itemReady($item, itemData, index) { // set up repeater items as they are created $item("#titleText").text = itemData.title; $item("#container").background.src = itemData.pic; } ``` ### Download Only the Fields You Need When retrieving data on a page, you can retrieve only the specific fields that you need. Oftentimes, you will be retrieving data from a collection or 3rd-party source, but only use a subset of the data you retrieve. On the server, in backend code, you can map the large set of fields to the smaller subset that you actually need, and then send only that smaller subset to the browser. For example, suppose you have a collection that contains a large number of fields. To display the information you have an index page that shows just a bit of information about each item. Then, when a site visitor clicks a link, you display the rest of the data on a page dedicated to displaying one item at a time.
When loading the index page, there is no reason to retrieve all of the data that exists in the collection for each item since you will only be displaying part of that data. Instead, for the index page, you can retrieve the collection data in a [backend web method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/call-backend-code-from-the-frontend.md). Then, in backend code on the server, you can map the retrieved data objects to objects with fewer properties, holding only the data you need. That way, a smaller amount of data needs to make the time-consuming trip from the server to the browser. ```javascript // in backend/queries.web.js import { Permissions, webMethod } from "wix-web-module"; export const getTeamData = webMethod(Permissions.Anyone, async () => { let results = await wixData.query("Team").find(); return results.items.map((item) => { return { _id: item._id, name: item.title, photo: item.photo, jobTitle: item.jobTitle, link: item["link-Team-title"], }; }); }); ``` Now, when you call your backend function from the browser, only the data you need is returned. You can use that data as usual to populate your repeater. ```javascript import { getTeamData } from "backend/queries.web"; $w.onReady(async function () { let data = await getTeamData(); $w("#teamRepeater").data = data; }); export function teamRepeater_itemReady($item, itemData, index) { $item("#teamImage").src = itemData.photo; $item("#teamMemberName").text = itemData.name; $item("#jobTitle").text = itemData.jobTitle; $item("#readMoreButton").link = itemData.link; } ``` When loading the item page, you don't need to add any extra code since you'll only be retrieving one item from your collection. ### Store Downloaded Data Instead of Refetching Sometimes, when a page is loading, it is better to download all the data you will eventually need and store it for later instead of downloading it incrementally as it is needed. This approach is often used when the data that is displayed will change based on a visitor interacting with it. Since you've downloaded all the data upfront, when you need to change the data that is displayed, the page transitions smoothly. If you would have to download new data every time a visitor interacts with the page, the page would react slowly and you might end up downloading the same data several times. To save some of the initial page loading time, you can combine this approach with only downloading the fields you need, as described above. For example, suppose you have an index page that displays a large number of items, but it is filterable, so they are not all shown at once. When a site visitor chooses a filter from a dropdown, the subset of items that you display changes.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/09/10/aa93f7b6-706c-4806-a191-087d0ce35269/1c6dafd7-a844-492d-89b2-46bbccd18641.png)
Here we can download all of the items when the page loads and store it in a global variable. When the filter changes, we don't have to download any new data. Instead, we just pull the relevant items from the data we already downloaded. Switching between filters will be very fast since all the work is done in the browser. Again, we retrieve the collection data in a [backend web method](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/backend-code/web-modules/about-web-modules.md#functions-in-web-modules). Then map the retrieved data objects to get only the fields we need. In this case, we added the **department** field. ```javascript import { Permissions, webMethod } from "wix-web-module"; export const getTeamData = webMethod(Permissions.Anyone, async () => { let results = await wixData.query("Team").find(); return results.items.map((item) => { return { _id: item._id, name: item.title, photo: item.photo, jobTitle: item.jobTitle, department: item.department, link: item["link-Team-title"], }; }); }); ``` Then, when the page loads, we call the backend function from the browser and store the retrieved data in a global variable. Every time we need to get a new subset of the data, we call the `getDepartmentData` function which filters out the data we don't need. We call it once when the page loads and then every time the value in the dropdown element changes. ```javascript import { getTeamData } from "backend/queries.web"; let teamData; $w.onReady(async function () { teamData = await getTeamData(); $w("#teamRepeater").data = getDepartmentData("Management"); }); function getDepartmentData(dept) { return teamData.filter((member) => member.department === dept); } export function departmentDropdown_change(event) { let department = event.target.value; $w("#teamRepeater").data = getDepartmentData(event.target.value); } export function teamRepeater_itemReady($item, itemData, index) { $item("#teamImage").src = itemData.photo; $item("#teamMemberName").text = itemData.name; $item("#jobTitle").text = itemData.jobTitle; $item("#readMoreButton").link = itemData.link; } ``` ### Pass Data Between Pages Often you use the same data on more than one page of your site. Instead of retrieving the data on each page that you need it, you can retrieve it just once the first time you need it. Then, you can use the **wix-storage-frontend** API to store the data to be used on other pages. For example, here we get some data that will be used on multiple pages. This code can be used on each of those pages, or you can add it to the site code if you need the data on all pages. When the page is loading we check the local storage to see if it already contains data. If it does, we simply parse the string data in a JSON object and store it in the data variable to be used elsewhere on the page. If the data doesn't already exist in storage, we query it from a collection, stringify it, and store it both in the data variable to be used elsewhere on the page and in local storage to be used on other pages. ```javascript import { local } from "wix-storage-frontend"; import wixData from "wix-data"; let data; $w.onReady(async function () { data = local.getItem("myData"); if (data) { data = JSON.parse(data); } else { let results = await wixData.query("myCollection").find(); data = results.items; local.setItem("myData", JSON.stringify(results.items)); } }); ``` In the example above we use local storage. Depending on your site's specific needs, you may want to use another type of storage. To do so, you simply substitute the type you want to use in the import statement. To learn more about the types of storage, see the [API Reference](https://www.wix.com/velo/reference/wix-storage.html). ### Use Dynamic Pages Sometimes, you create a page with data that doesn't necessarily need to be a dynamic page. Instead, you can use a regular page and add a dataset to it. For example, if you have an index page that shows all the elements from a collection, you can use a regular page with a dataset to retrieve and display your collection data. In such cases, it is often better to use a dynamic page anyway. You can set the dynamic page without any fields added to its URL so it does not filter the data and the page receives all your collection items. Because your page is a dynamic page, it is known that the page expects data and the data is therefore retrieved while the page is being rendered. On the other hand, when you use a regular page, data is retrieved much later. ### Use Bulk Data Operations When possible, use the bulk operations of the **wix-data** API instead of repeatedly calling the single-item version of the function. For example, instead of repeatedly calling [`insert()`](https://www.wix.com/velo/reference/wix-data.html#insert) to add an array of items to a collection one-by-one, you can call [`bulkInsert()`](https://www.wix.com/velo/reference/wix-data.html#bulkInsert) and add all of the items at once. ### Add Indexes to Your Data Collection
**Important:** Adding an index to a collection is in open beta and not yet available to all users.
You can add [indexes](https://support.wix.com/en/article/velo-indexes-and-wix-data-collections) to your data collection to optimize the performance of database queries and improve data retrieval speed. Without indexes, a query runs filter and sort operations that iterate through every item in the collection. For a small collection, the query time might be negligible. But as the quantity of data increases, each query takes longer to process. When you [add an index](https://support.wix.com/en/article/content-manager-adding-an-index-for-your-collection), you are providing a map of the collection's data based on specific database fields. The database uses this map to quickly identify and retrieve the rows that match a particular value, significantly reducing query response times. You can also improve the performance of sorting operations with indexes. When you create an index on the column being sorted, you enable the database to arrange data in the desired order without performing a full table scan, reducing sort times and improving query performance. Note that while indexes can enhance data retrieval speeds, they can slightly slow down write operations because these operations include updating indexes for each change in addition to making the actual change. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Contact Us ## Article: Contact Us ## Article Link: https://dev.wix.com/docs/develop-websites/articles/contact-us/contact-us.md ## Article Content: # Contact us Can't find an answer and need some help? There’s a couple of ways to get in touch. - [Contact support](https://www.wix.com/support-chatbot?nodeId=3809b091-762a-4a1a-acd7-a6b0dabc9c5a&referral=veloLeftTree): Open a ticket and we'll get back to you within 72 hours. - [Join us on Discord](https://discord.gg/aN9ubnrs2D): Connect with our team and network with other site developers. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Submit Support Requests ## Article: Submit Support Requests ## Article Link: https://dev.wix.com/docs/develop-websites/articles/contact-us/submit-support-requests.md ## Article Content: # Submit Support Requests As you develop and maintain your site, you may encounter error messages, technical glitches, or unexpected behavior. In these cases, you can open a support request using the Wix support chatbot. Follow these steps to make a successful support request. [](https://www.wix.com/support-chatbot?nodeId=3809b091-762a-4a1a-acd7-a6b0dabc9c5a&referral=veloLeftTree) ## Step 1 | Retrieve your site URL Before engaging with the Wix support chatbot, retrieve your site's URL. ## Step 2 | Locate your request ID (recommended) If the issue results from using Wix APIs in your site's code, interacting with the site dashboard, or through one of Wix's [frontend interfaces](https://dev.wix.com/docs/develop-websites/articles/get-started/the-wix-ecosystem.md), you must include the associated request ID. Every action that makes a call to Wix’s APIs is assigned a unique request ID, which is essential for tracking the root cause of an issue. This ID is included as a response header with the key name `x-wix-request-id`. For example, `1734427301.191556792796277844`. Many errors in the site dashboard display pop-up notifications at the top of the screen. These notifications often include the request ID. If you don’t see a request ID in an error notification, you need to recreate the issue to retrieve a request ID.
For issues resulting from your site's API requests to Wix: 1. Make the request again using `curl` or any other network request tool that allows you to view responses. 2. In the response header, locate the request ID under the key `x-wix-request-id`.
For issues occurring in the site dashboard, app dashboard, or site editor: 1. Trigger an action that causes the error. For example, by clicking a button. 2. Open your browser's developer tools. 3. Select the **Network** panel. 4. In the **Name** column, click the network request associated with the error. Additional request information appears. 5. Select the **Headers** tab. The request ID is located in the **Response Headers** section under the key `x-wix-request-id`.
## Step 3 | Describe the issue When the support chatbot asks for a description and the option to attach documents, you should be prepared to provide a step-by-step description of what led to the issue. Compile as many relevant details as possible, such as: - A short explanation of the issue. - Screenshots and/or videos of each stage of the process. - URLs of the pages involved in the issue. - Configuration and setup information associated with each step.
Caution: Do not include any secret keys in your description.
- Include the complete request and response of any failed network requests. - Relevant console error logs. ## Step 4 | Open the request Now you're ready to open your support request. 1. Open the [Wix support chatbot](https://www.wix.com/support-chatbot?&nodeId=e0bd5aa6-d9d8-4627-a981-17dbbb0c78db&referral=discord). 1. Begin interacting with the chatbot. Provide the information you gathered above whenever the bot requests it. --- ## [Develop Websites with SDK](https://dev.wix.com/docs/develop-websites-sdk.md) > Extend Wix sites using the Wix SDK for frontend and backend development with React-based blocks > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Developing Websites ## Article: About Developing Websites ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-developing-websites.md ## Article Content: # About Developing Websites Developing websites on Wix with the [JavaScript SDK](https://dev.wix.com/docs/sdk.md) offers a unique blend of flexibility, ease of use, and powerful features. Whether you're a seasoned developer or just starting, Wix provides the tools and technologies needed to craft visually appealing and fully functional dynamic web applications: - **Interactive UI:** Use Wix's powerful editors to build sites with custom UI, behaviors, and interactions. - **Full-stack development:** Leverage a robust suite of developer tools and Wix APIs to build and deploy with efficiency and security. Integrate your code with the editor UI to enhance and extend its capabilities. - **Content management:** Use the Wix CMS and its data APIs to manage, serve, and display content dynamically at scale. - **Wix business solutions:** Connect directly to production-ready solutions like eCommerce and Bookings, and extend them to deliver a tailored experience for site visitors. - **Zero-setup backend:** Implement complex workflows and automations with a fully supported Node.js environment. Wix handles infrastructure, hosting, and scaling so you can focus on your code. - **3rd-party integrations:** Expand a site's capabilities by adding apps, npm packages, and external services such as payment providers and marketing tools. ## Technologies Build with trusted, industry-standard tools tailored to Wix's platform. Utilize your existing skills and leverage the following technologies to create and manage dynamic web applications: - **JavaScript**: When developing a site with the SDK, you write both frontend and backend code in JavaScript, calling the SDK's APIs to incorporate interactive and dynamic functionality. - **Node.js**: Wix provides a robust Node.js environment for running your backend code. - **MongoDB**: Wix uses MongoDB as the database for its CMS system, offering a flexible and scalable solution for managing and storing content. - **Type safety**: The SDK provides full TypeScript support with comprehensive type definitions for enhanced development experience. ## Development environments Pick the environment that fits how you build. Wix offers several [development environments](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/development-environments.md) to match your workflow: - **Wix Studio:** Use the built-in [code editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) in the browser to write and manage code alongside the visual site editor. No setup required. - **Local IDE:** Use a [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md), such as VS Code, for the flexibility and power of a full-featured development environment on your own machine. Set up via GitHub integration and manage your project using the CLI. Pair with [AI tools](#wix-ai-tools) to provide local AI coding clients the context they need to build and manage your project. - **Wix IDE (Beta):** Use the [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md), a VS Code-based online IDE for Wix Studio sites. ## Wix AI tools For AI coding clients like Claude Code, Cursor, and VS Code, install the [Wix Plugin](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-the-wix-plugin.md) to give your AI client the context it needs to build and manage your project. The Wix Plugin includes: - [Wix Skills](https://github.com/wix/skills/blob/main/skills): A collection of self-contained instruction sets for Wix development tasks. This includes the Wix Headless skill, which can scaffold and deploy a complete headless project from a single prompt. - [Wix MCP](https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/about-the-wix-mcp.md): A Model Context Protocol server that lets your AI tool search Wix docs, scaffold code, and call Wix APIs directly from your IDE. To install just Wix Skills, run: ```bash npx skills add wix/skills ``` > **Note:** Add `-g` to install globally and make Wix Skills available across all your projects. ### Programmatic docs access Beyond the Wix Plugin, you can pull Wix docs into any tool or workflow: - **llms.txt index:** Browse a structured index of all documentation at [dev.wix.com/docs/llms.txt](https://dev.wix.com/docs/llms.txt.md). - **Markdown format:** Append `.md` to any documentation page URL to retrieve the Markdown version of that page. - **Page menu:** Use the **Ask Assistant** dropdown on any documentation page to copy the page as Markdown or get a direct link to the Markdown version. ## Developer journey Ready to get started? Here's what to expect at each stage of the development process, from a blank canvas to a fully functioning site: - **Get started:** Explore the [Wix ecosystem](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/the-wix-ecosystem.md) and [supported features](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/site-development-features.md). Browse the [API Reference](https://dev.wix.com/docs/api-reference?apiView=SDK.md) and review potential [3rd-party integrations](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/integrate-with-3rd-parties.md) to see what you can build. Take some time to review best practices and any limitations to ensure a smooth development process. - **Code:** [Start coding](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-java-script-support.md) using 1 or more Wix [development environments](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/development-environments.md) to write, manage, and debug your code. Build both frontend and backend to deliver a fully functional and user-friendly site. - **Test:** Preview and thoroughly test your site throughout development. Use the built-in tools to test your code in isolation and as a whole. Create [test sites](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md) to experiment without affecting a live site, and use [logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md) to monitor and debug issues. - **Publish:** Use the editor or the [CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) to publish your site once you've thoroughly tested it. - **Maintain:** Monitor your site using [logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md) and the [monitoring dashboard](https://dev.wix.com/docs/develop-websites-sdk/maintain-your-site/monitor-your-site/about-the-monitoring-dashboard.md) to track performance and resource usage. Consider [upgrading your premium plan](https://dev.wix.com/docs/develop-websites-sdk/maintain-your-site/scale-your-site/about-premium-plans.md) to scale your site effectively and avoid hitting plan limitations. - **Grow:** Become a [Certified Wix Partner](https://www.wix.com/partners/partner-program) to offer your development services or sell apps to an audience of 250M+ users. ## Community - Join the [Wix Developers Discord server](https://discord.gg/47gUT9KabP) and [forum](https://forum.wixstudio.com/) to ask questions, discuss complex topics, and collaborate with other Studio creators. - Contact [customer care](https://www.wix.com/contact) for support with platform issues, billing, or account questions. - Follow the Wix [changelog](https://dev.wix.com/docs/changelog.md) for up-to-date information about Wix features and APIs. - [Subscribe](https://www.wixforms.com/f/7394355343209268224) to the Wix newsletter to receive the latest updates and feature announcements. ## Next steps Start building or explore the resources below to go deeper: - Explore [the Wix Ecosystem](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/the-wix-ecosystem.md) and browse the [SDK Reference](https://dev.wix.com/docs/sdk.md) to understand the platform and what you can build. - Browse [tutorials](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/tutorials-overview.md) and [code examples](https://dev.wix.com/docs/coding-examples.md) for step-by-step guidance on implementing functionality for a site. - Follow the [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) to start building. Coming from Velo? Learn how to [migrate from Velo to the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/velo-to-sdk-api-mapping.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: The Wix Ecosystem ## Article: The Wix Ecosystem ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/the-wix-ecosystem.md ## Article Content: # The Wix Ecosystem Before writing code for a site, it's important to have a basic understanding of the greater Wix ecosystem. Once you understand what each part of the ecosystem does and how they work together, you'll be ready to start writing code that interacts with the various parts of the ecosystem. The Wix ecosystem includes a range of tools and services that enable you to build a web presence and manage your business online. The following diagram shows the main parts of the Wix ecosystem: ![Wix ecosystem diagram](https://wixmp-833713b177cebf373f611808.wixmp.com/images/45a8810238260dae1fdc35cebe2d786b.png) ## Site creation Wix offers multiple approaches for creating websites, each designed for different needs and technical preferences. ### Drag-and-drop editors Wix offers the following drag-and-drop editors: - [Wix Studio](https://support.wix.com/en/wix-studio): An advanced web creation platform for agencies, freelancers, and enterprises. - [Wix Editor](https://support.wix.com/en/using-the-wix-editor): Wix's classic site builder. - [Wix Harmony](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-wix-harmony.md): An AI-powered editor designed for intuitive web creation. Note that Wix Harmony doesn't support custom code. Learn more about [Wix Harmony](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-wix-harmony.md). These editors offer a visual, drag-and-drop interface with AI-powered features that lets users: - Add and customize site pages, elements, widgets, and more. - Customize site functionality with code. - Manage site content including text and media. - Add and manage Wix business solutions, such as Wix Stores or Wix Bookings. - Add and manage apps built by 3rd-party developers. Sites built with these editors have a Wix-powered frontend. These are the sites documented in the [Develop Websites](https://dev.wix.com/docs/develop-websites-sdk.md) portal. Here's what Wix Studio looks like: ![Wix Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/602c65a5abe17eba3931fee886a3cbf8.png) ### Other site creation approaches While the Develop Websites documentation focuses on drag-and-drop editor development, Wix offers additional site creation approaches: - **[Wix Vibe](https://support.wix.com/en/article/wix-vibe-an-overview)**: A conversational website creation tool that builds sites through natural language prompts. While Vibe includes visual editing components, these aren't as fully-featured as the dedicated drag-and-drop editors. Sites built with Vibe take the form of Wix-managed Headless sites, with a React based frontend and use the Astro framework. - **[Wix Headless](https://dev.wix.com/docs/go-headless.md)**: Enables developers to build custom frontends using any technology while leveraging Wix's backend infrastructure. Sites built with Wix Vibe or Wix Headless are documented in the [Go Headless](https://dev.wix.com/docs/go-headless.md) portal. ## Site management Once you create a site, Wix provides tools for ongoing management and operation. ### Site dashboard The dashboard is the site's control center. Whether a site was built using the drag-and-drop editors, created with Vibe, or developed as a headless project, all sites use the same unified dashboard. Only you and your collaborators have access to the dashboard. Most dashboard functionality comes out of the box and enables you to set up, manage, and operate a site and its installed apps. You can also design and code custom dashboard pages. In the dashboard, you can: - Configure site settings. - Manage site content including text and media. - Manage payments, customers, online marketing, automations, and more. - Add and manage business solutions, such as Wix Stores or Wix Bookings. - Add and manage apps built by 3rd-party developers. - Monitor site metrics, such as SEO performance, sales reports, and site traffic. Here's what the dashboard looks like: ![Wix dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/46cca8591e446c4e032473b4c8b65301.jpeg) ## Development and integration Wix's platform offers an extensive array of services and tools for developers to integrate with and extend the Wix ecosystem. ### APIs Wix APIs give you access to Wix's services and resources, providing a gateway that enables you to easily integrate with different areas of the Wix ecosystem. APIs include: - **Business solutions**: Interact with Wix's business solutions, such as Stores, Bookings, Events, and Restaurants. - **Payments**: Manage payments with Pricing Plans, Payments, and Billing modules. - **CRM and Members**: Interact with and manage site contacts and members. - **Data**: Work with data stored in Wix-hosted and external databases using type-safe operations. - **Automations**: Streamline work processes and send notifications. - **Web methods**: Create secure backend functions accessible from the frontend. ### CMS Wix's Content Management System (CMS) is where you manage your site's content, such as text, media, videos, products, and booking services. The CMS seamlessly integrates with Wix's site-building tools, providing a user-friendly interface for creating and editing content, as well as mechanisms for easily connecting CMS data to site elements. With the SDK, you can programmatically interact with CMS content using type-safe APIs. ### Wix business solutions At the heart of Wix's backend are its native business solutions, which cater to diverse industries with advanced solutions for bookings, online stores, events, payments, eCommerce, loyalty programs, and more. These apps built by Wix are designed to integrate seamlessly with Wix websites, as well as with external clients that take advantage of Wix's [headless](https://dev.wix.com/docs/go-headless.md) infrastructure. The JavaScript SDK provides dedicated modules for interacting with these business solutions. ### Apps When building a site, you can install apps built by Wix or 3rd-party app developers from the [Wix App Market](https://www.wix.com/app-market/) to add pre-built functionality to your site. #### 3rd-party apps Wix apps are packages of reusable functionality, created by 3rd-party vendors, that you can add to your site. They can contain anything from a simple site widget to a complete business solution, with backend logic, user interfaces, and database collections. Wix apps are listed in the [Wix App Market](https://www.wix.com/app-market/). You can also [build your own apps](https://dev.wix.com/docs/build-apps.md) to share functionality across multiple sites or to offer functionality you've built to other Wix users. ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) - [Development Environments](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/development-environments.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Site Development Features ## Article: Site Development Features ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/site-development-features.md ## Article Content: # Site Development Features Wix offers a robust suite of features that support an efficient and productive development workflow. This overview covers the key features that help you develop, test, publish, and maintain custom sites with advanced functionality. ## Developer environment Leverage Wix's suite of tools and IDE options designed for efficient site development. **IDE options**: Choose from the code editor built into Wix Studio and the Wix Editor, the online Wix IDE, or a local IDE like VS Code. **Git integration and Wix CLI**: When working in a local IDE, use Git and the Wix CLI to manage version control and local development, allowing easy collaboration, change tracking, and branching. **Secrets Manager**: Store sensitive information securely with the Secrets Manager, which keeps API keys and other sensitive credentials hidden from your code. **Packages and apps**: Add functionality with npm packages, Velo packages, or custom apps tailored to your project's needs. **AI Assistants**: Use AI-powered tools to speed up development, offering code suggestions, debugging support, and productivity enhancements. ## Build a custom backend Develop backend code, hosted by Wix, to build advanced features and integrations, all managed securely on the platform. **Web modules**: Write backend code that securely handles business logic and can easily be called from the frontend. **Recurring jobs**: Automate tasks, such as data updates and email notifications, to run on a regular schedule. **HTTP functions**: Expose your site functionality as a service by creating endpoints that external applications can call, opening up integration possibilities. **Routers**: Take full control of your site's URL structure and routing by customizing how requests to specified paths are handled. **Event handlers**: Add backend event handlers to respond to actions that occur on your site, such as a new contact being created or a customer abandoning their cart. ## Build a custom frontend Enhance the pages you've designed in the Wix editor by adding custom code to create interactive and dynamic user interfaces, allowing you to fully tailor the user experience. **$w API**: The $w API lets you interact with UI elements. With support for over 100 element types, from simple elements like images, buttons, and input fields to advanced components like repeaters, galleries, and multi-state boxes, the $w API enables you to add interactivity, manage user input, and customize element behavior on the page. **CSS**: Use CSS to customize the appearance of your site, from colors and fonts to sizes and positions of elements. Work with the existing Wix CSS classes or create your own custom classes. **Custom elements**: Create custom HTML elements and integrate them seamlessly into your site, enabling unique functionality and design beyond what the built-in components provide. ## Data Manage your site's content and data with Wix's integrated database solutions, allowing for dynamic, data-driven web experiences. **Wix CMS**: Manage structured content with the Wix CMS, allowing you to store, query, and display data programmatically or using no-code features. **External databases**: Connect to external databases, so you can integrate data from other sources using the same APIs and no-code features that you use with native Wix data. **Dynamic pages**: Use dynamic pages to create multiple pages from a single design by connecting page elements to database content. Use the Dataset API to programmatically fine-tune the functionality of dynamic pages. **Data API**: Access and manipulate CMS and external content programmatically with the Data API, enabling you to create, read, update, and delete data directly from your code. ## Wix business solutions Leverage Wix's robust suite of business solutions to incorporate features such as eCommerce and bookings, while having the flexibility to extend and integrate with these solutions to fit your business needs. **Wix business solution APIs**: Integrate directly with Wix's business solutions, such as Wix Bookings and Wix Stores, using dedicated APIs to access and manage bookings, orders, and more. **Service plugins**: Enhance Wix business solutions by creating custom service plugins that inject your own custom logic or integrate 3rd-party services into Wix flows. For example, you can create a service plugin to customize price calculations for services booked using Wix Bookings or to add an unsupported payment provider as an additional payment option. **Replace business solution pages**: Customize the user experience by replacing default Wix business solution pages with your own, giving you full control over design and functionality. For example, you can replace the default Wix Bookings service page with your own custom version. Your page will appear in the booking flow instead of Wix's default page. ## Test your site Ensure your site performs optimally and provides a seamless user experience by utilizing Wix's testing features to identify and rectify issues before going live. **Preview**: Use preview mode in the editor to test your code in a controlled environment. You can interact with a preview version of your site and see errors and logs in the developer console, without affecting the live version of your site. **Functional testing**: Run tests on backend methods in isolation to ensure they work as expected, without having to write extraneous testing code. **Test sites**: Create a dedicated test site to experiment with features, test integrations, and refine functionality without impacting your live site. This is especially useful for complex updates or collaborative testing. ## Maintain your site Keep your site updated and running smoothly with Wix's maintenance tools, ensuring long-term performance and security. **Monitoring**: Keep track of your site's performance, monitor your storage usage, and troubleshoot site issues with the [monitoring dashboard](https://dev.wix.com/docs/develop-websites-sdk/maintain-your-site/monitor-your-site/about-the-monitoring-dashboard.md). Keeping tabs on monitoring data allows you to proactively address issues and ensure a smooth user experience. **Logs**: Access detailed logs to review site activity, debug issues, and gain insights into user interactions. You can view logs in real time within Wix and connect to Google Cloud Logs to store logs and perform analysis, generate visual depictions of log data, and to extract meaningful insights from your logs. ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) - [The Wix Ecosystem](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/the-wix-ecosystem.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Development Environments ## Article: Development Environments ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/development-environments.md ## Article Content: # Development Environments When writing code, having the right development environment can greatly enhance your productivity. Wix provides development options that let you work directly in Wix or work with the tools you already use. The development environments are: - The built-in [code editor](#code-editor) in Wix Studio or the Wix Editor. - The online [Wix IDE](#wix-ide) for Wix Studio sites. - A [local IDE](#local-ide) of your choice, such as VS Code, when using Wix Studio or the Wix Editor. You can use a local IDE, in conjunction with the Wix CLI, after connecting your site to GitHub. The development environment you should choose depends on your project needs and workflow. Each option offers unique advantages, and you can mix and match environments to some extent as needed. > **Note**: Some Wix website development features are only available in specific development environments. See each feature’s documentation for a list of development environments that support it. ## Code editor The built-in code editor is a browser-based environment available directly in the Wix Studio or Wix Editor. It provides an easy way to write and manage code alongside the visual site editor, enabling you to write code without leaving the Wix platform. ![Code editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/edd17d777e317076582b7df9b22178ed.png) ## Wix IDE The Wix IDE is an advanced, VS Code-based online development environment available for Wix Studio sites. The Wix IDE opens in a separate browser tab or window with hot reloading. This gives you more space for both coding and site editing. Having a window to design and preview and a second window to code is especially useful if you're working with a multi-monitor setup. The Wix IDE also enables concurrent editing, allowing multiple developers to work in the Wix IDE at the same time. When working in the Wix IDE, you can utilize the [Wix AI Assistant](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ai-assistants/about-ai-assistants.md#wix-ai-assistant-in-the-wix-ide), an intelligent companion that offers real-time coding assistance. ![Wix IDE](https://wixmp-833713b177cebf373f611808.wixmp.com/images/80f327c068d3c46954bf4d585e6b3b1d.png) ## Local IDE Use a local IDE, such as VS Code, for the flexibility and power of a full-featured development environment on your own machine. With a local setup, you can customize your environment and use the extensions and shortcuts that you use for all your non-Wix projects. To support local development, use the Wix CLI, which connects your local code to your site through GitHub. Once you connect a site to GitHub, you can only edit the site’s code in your local IDE. The CLI, along with a local editor, allows you to write, test, sync, and publish your code from your local environment. Because your code is stored in a Git repository, you can manage branches, versions, and team collaboration just like you would for any other Git project. ![Local IDE](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8c45c4e13c906cf619e1d4a0eb69565e.png) ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) - [The Wix Ecosystem](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/the-wix-ecosystem.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate with Wix Business Solutions ## Article: Integrate with Wix Business Solutions ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/integrate-with-wix-business-solutions.md ## Article Content: # Integrate with Wix Business Solutions Wix offers a range of powerful business solutions, enabling you to sell products and services, run events, write blogs, manage restaurants, and more—all seamlessly integrated with your website. These solutions are built to work out-of-the-box on Wix sites, but with custom code, you can extend their capabilities and tailor them to fit your specific needs. Your site can integrate with Wix's business solutions in the following ways: - [Wix business solution APIs](#wix-business-solution-apis): Leverage functionality from Wix's business solutions like Wix Stores and Wix Bookings. - [Service plugins](#service-plugins): Inject custom functionality or 3rd-party integrations into Wix business solutions. - [Replace business solution pages](#replace-business-solution-pages): Customize user experience by replacing Wix's default business solution pages with your own. ## Wix business solution APIs Wix business solution APIs allow you to programmatically interact with Wix's business solutions, such as Wix Stores and Wix Bookings. This enables you to extend their capabilities, integrate with external systems, and create custom functionality tailored to your site's specific requirements. For example, you can use Wix business solution APIs for: - **Personalized recommendations**: Use customer data to generate personalized Wix Stores product recommendations on your site. - **Custom service availability UI**: Retrieve and display the availability status of Wix Bookings services in a custom UI. - **Customer data sync**: Sync Wix Member profiles and purchasing history with data from an external CRM system for a unified customer view. ## Service plugins [Service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md) allow you to inject additional functionality or integrate 3rd-party services directly into Wix's business solutions. Plugins consist of functions that are called automatically by Wix apps at defined points in their flows. These functions receive and return data in specific formats, enabling seamless integration with the Wix app's business logic. For example, you can use service plugins for: - **Custom shipping rate**: Integrate 3rd-party logistics services to provide customized shipping rates in Wix Stores. - **Custom bookings pricing**: Implement dynamic pricing models for services in Wix Bookings, such as surge pricing or member-only discounts. - **Custom payments provider**: Integrate a 3rd-party payment gateway to offer additional payment options for customers in Wix Stores. ## Replace business solution pages Customize the user experience by replacing default Wix business solution pages with your own. Design a page and add functionality to it using the $w API for page elements and the Wix business solution APIs. When the business solution flow reaches the predetermined step, your page is presented to site visitors instead of the usual Wix page. Once visitors finish interacting with your page, you redirect them back to the next Wix page in the flow. For example, you can replace the following pages with your own versions: - **Bookings service page**: Replace the default Wix Bookings service page with a custom-designed page that includes unique features, such as custom booking options and additional information specific to your services. - **Stores product page**: Replace the default Wix Stores product page with a custom version where you can showcase your products with enhanced visuals, custom layout, and unique interactive elements to improve the shopping experience. - **Custom plans & pricing page**: Replace the default Plans & Pricing page from the Wix Pricing Plans app with a custom-designed page that highlights your pricing plans with a unique layout and interactive pricing details to better attract and convert visitors. ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) - [Site Development Features](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/site-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate with 3rd Parties ## Article: Integrate with 3rd Parties ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/integrate-with-3rd-parties.md ## Article Content: # Integrate with 3rd-Parties Integrating 3rd-party services into your site allows you to save time, extend functionality beyond native features, and connect seamlessly with existing systems you have already invested in. Your site can integrate with 3rd-party solutions in the following ways: - [Fetch API](#fetch-api): Connect with 3rd-party APIs securely. - [npm packages](#npm-packages): Leverage 3rd-party functionality within your Wix site. - [HTTP functions](#http-functions): Expose your site's functionality so 3rd-parties can call your site's APIs. - [Service plugins](#service-plugins): Inject 3rd-party services into Wix business solutions. ## Fetch API Use the standard Fetch API to connect your site to 3rd-party APIs. Store sensitive credentials, such as API keys, in the Secrets Manager to keep your API calls secure. For example, you can use the Fetch API for: - **Weather updates**: Display live weather updates by fetching data from a weather service API. - **Inventory sync**: Keep your store inventory synced with a 3rd-party inventory management system. - **Analytics**: Send custom event data to an analytics service. ## npm Packages With [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) you can leverage a vast array of prebuilt 3rd-party modules to extend the functionality of your site. By integrating these packages, you can easily add complex features without having to build them from scratch. For example, you can use npm packages for: - **PDF generator**: Use a PDF generator module to dynamically create and customize PDF documents on your site. - **SMS messaging**: Send SMS notifications to visitors for order confirmations, appointment reminders, or promotional messages. - **QR code generation**: Integrate a module to dynamically generate QR codes for promotional offers or mobile app links. ## HTTP functions With [HTTP functions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md) you can expose your site as a service and allow 3rd-party systems to interact with your site data and functionality. For example, you can use HTTP functions for: - **Webhooks**: Set up an HTTP function to receive and process webhooks from external services, such as payment notifications or delivery updates. - **Alternative frontends**: Use HTTP functions to expose the logic and data of your site to alternative frontends such as mobile apps, smartwatch apps, or other IoT devices. - **Automation services**: Integrate your Wix site with automation services. You can create endpoints that automation platforms can trigger as part of an automation workflow. ## Service plugins [Service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md) allow you to inject additional functionality or integrate 3rd-party services directly into Wix's business solutions. For example, you can use service plugins for: - **Custom shipping rate**: Integrate 3rd-party logistics services to provide customized shipping rates in Wix Stores. - **Custom bookings pricing**: Implement dynamic pricing models for services in Wix Bookings, such as surge pricing or member-only discounts. - **Custom payments provider**: Integrate a 3rd-party payment gateway to offer additional payment options for customers in Wix Stores. ## What's next - [Quick Start](https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md) - [Integrate with Wix Business Solutions](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/integrate-with-wix-business-solutions.md) - [Site Development Features](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/site-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Wix Harmony ## Article: About Wix Harmony ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-wix-harmony.md ## Article Content: # About Wix Harmony Wix Harmony is a new Wix Editor that offers a flexible, intuitive platform for building sites. It brings together advanced AI tools, including Aria for prompt-based creation, paired with freeform drag-and-drop capabilities. Harmony is designed for self-creators who want an AI-assisted site building experience without code. Wix Harmony doesn't support site code. If your projects require code customization, continue using [Wix Editor](https://www.wix.com) or [Wix Studio](https://www.wix.com/studio). However, you can use [custom code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/custom-code/about-custom-code.md) to add HTML, JavaScript, and CSS snippets to Harmony sites. > **Note:** If you're building apps with Wix Blocks, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md). ## Existing sites Existing Wix sites with site code will continue to work normally. You can still access and edit your sites and code through the Wix Editor or Wix Studio. Existing sites are not being migrated to Harmony. ## Build with Wix Studio For projects that require site code, Wix Studio is your development platform. Wix Studio supports: - Full coding capabilities with the Wix JavaScript SDK and Velo APIs. - Pixel-perfect layouts with responsive design systems. - Collaboration tools for working with team members. - Professional tooling including IDE integration and Git workflows. Learn more about [Wix Studio](https://www.wix.com/studio). ## Features not available in Wix Harmony Features not available in Wix Harmony include: - [Custom site code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md), including page code and backend code - [Custom CSS](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md) - [HTTP functions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md) - [Routers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) - [Data hooks](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md) - [Backend event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md) - [Web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) - [Scheduled jobs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/scheduled-jobs/about-scheduled-jobs.md) ## What's next We're working on bringing the AI creation experience to Wix Studio. The new features aim to give you the best of both worlds: powerful AI tools combined with full development capabilities. Stay tuned for updates. ## Get support We're here to answer your questions and concerns. [Contact us](https://www.wix.com/support-chatbot?nodeId=3809b091-762a-4a1a-acd7-a6b0dabc9c5a&referral=veloLeftTree). ## See also - [About Developing Websites](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-developing-websites.md) - [About Wix Harmony and Apps](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md) - [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md) - [About Custom Code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/custom-code/about-custom-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Glossary ## Article: Glossary ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/glossary.md ## Article Content: # Glossary ## $w $w is the selector syntax you use to select elements in code (for example, `$w('#myButton')`). After selecting an element, use APIs to work with its properties and functions. ## Blocks [Wix Blocks](https://support.wix.com/en/article/creating-a-velo-package-3964385) allows you to build [apps](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/get-started/a-blocks-app-workflow.md), and reuse your code across multiple Wix sites. ## CMS role Together with [collection permissions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md#permissions), [CMS roles](https://support.wix.com/en/article/cms-formerly-content-manager-creating-custom-roles-and-permissions) give you control over which visitors are allowed to interact with the data in your collections and what they're allowed to do. Every person that visits your site falls into one of 4 roles: admin, site member author, site member, and anyone. You can assign specific permissions to each role. ## Database collection A [database collection](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md) is a table used on a Wix site. Each row is an item, and each column is a field, including: - [System fields](https://support.wix.com/en/article/about-database-collections#system-fields): Auto-added, hidden, non-editable fields, including ID, Date Created, Last Updated and Owner fields. - [Primary field](https://support.wix.com/en/article/cms-using-reference-fields-to-display-content-from-multiple-collections): Text field used for references between collections, marked with a lock icon. Default is Title, but any text field can be set as the primary field. - Field ID: Immutable identifier used in code for a field, different from the Field Name shown in the UI. You can connect elements in your site to fields in your collection using [connecting to data](https://support.wix.com/en/article/connecting-page-elements-to-data) and [datasets](#dataset). [Permissions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md#permissions) control which site [roles](#roles) (Anyone, Site member, Site member author, Admin) can create, read, update, and delete items in a collection. Permissions apply to the collection, not individual fields, and work together with the dataset's [mode](https://support.wix.com/en/article/working-with-dataset-modes-and-collection-permissions). ## Database schema/structure A [database schema](https://support.wix.com/en/article/about-database-collection-schemas) is the definition of a database's structure. The schema defines which fields are in each of your collections and their [type](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-types). However, in some cases, your collections can contain fields that aren't part of the schema. ## Dataset A [dataset](https://support.wix.com/en/article/cms-about-datasets) is a component that connects page elements to your [collections](#database-collection). It selects the collection and controls sorting, filtering, and what elements can do via the [dataset's mode](https://support.wix.com/en/article/working-with-dataset-modes-and-collection-permissions) (Read & Write, Read-only, Write-only). Works on regular and dynamic pages and isn't displayed on the live site. ## Dynamic page A [dynamic page](https://support.wix.com/en/article/cms-about-dynamic-pages) uses a single layout to display different items from a collection. Content is determined by the page URL - its [prefix](https://support.wix.com/en/article/url-prefixes-and-page-grouping) and dynamic segments are saved on each item as a [calculated field](https://support.wix.com/en/article/about-calculated-fields). There are 2 types of dynamic pages: [dynamic item page](https://support.wix.com/en/article/cms-setting-up-a-dynamic-item-page) and [dynamic list page](https://support.wix.com/en/article/cms-setting-up-a-dynamic-list-page). ## Dynamic page dataset A [dynamic page dataset](https://support.wix.com/en/article/about-data-binding-and-datasets#about-dynamic-page-datasets) is a component that's added to your page automatically when you create a dynamic page. Like a regular [dataset](#dataset), it enables you to connect the elements on your page to your collections. It also allows you to add more filters and to sort the content on your page. ## Functional testing With [functional testing](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/about-functional-testing.md), you can easily and quickly [test and debug your backend code directly in the code editor](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/test-backend-functions-with-functional-testing.md). Functional Testing saves you the time and effort of building your own system for triggering and testing your backend functions. ## HTTP function An [HTTP function](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md) allows you to expose your site's functionality as an HTTP endpoint. Call these endpoints from external services or apps to integrate with your Wix site. ## Hook A [hook](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md) runs code before or after certain operations. Use data hooks to intercept collection operations, or router hooks to customize dynamic page data binding. ## Live database The [live database](https://support.wix.com/en/article/managing-your-sandbox-and-live-data#live-data) is your published site's data storage. It appears on the live site and stores data collected there. You can [sync](https://support.wix.com/en/article/cms-accessing-and-syncing-your-sandbox-and-live-collections) between sandbox and live (copy sandbox to live, or overwrite sandbox with live) from the sandbox. Sandbox and live aren't always enabled for your collections. Learn more about [sandbox, live collections and syncing](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Monitoring dashboard Use the [monitoring dashboard](https://dev.wix.com/docs/develop-websites-sdk/maintain-your-site/monitor-your-site/about-the-monitoring-dashboard.md) to track site performance, especially for backend code and data requests. Access it from Developer Tools in the Code sidebar. You can also use [Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md) to monitor the site's performance. ## Properties panel Use the [properties panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md) to configure elements, add [event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) and set default behavior on page load. ## Regular editor The remote Wix Editor for editing and previewing your site's UI and code, as opposed to the [local editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md) when using [Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). ## Release manager Use the [Release Manager](https://support.wix.com/en/article/accessing-the-release-manager) to view the current published version and [test site](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md), and to create, edit, or delete test versions. ## Repeater A repeater reuses a single layout for multiple items, where each item has the same layout with different content. Use static content set in the Editor, or connect to the [data in a collection](https://support.wix.com/en/article/displaying-database-content-in-a-repeater) to display data-bound lists. ## Revision Every time you manually save or publish your site following a change, a new revision of your site is created. You can view and manage your revisions in your [Site History](https://support.wix.com/en/article/viewing-and-managing-your-site-history). You can also create a [test site](https://support.wix.com/en/article/creating-a-test-site) or [monitor Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md) for a specific revision of your site. ## Router A [router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) lets you control how your site handles incoming requests and SEO. Customize routing logic and the content returned for specific paths. ## Sandbox database The [sandbox database](https://support.wix.com/en/article/managing-your-sandbox-and-live-data) is your development data storage. Its data doesn't appear on the published site unless you [sync](https://support.wix.com/en/article/cms-accessing-and-syncing-your-sandbox-and-live-collections) to the [live database](https://support.wix.com/en/article/managing-your-sandbox-and-live-data#live-data). Preview mode updates sandbox data. Sandbox and live aren't always enabled for your collections. Learn more [here](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Selector scope A scope defines which elements you can select with a selector function, such as [$w( )](#w), and how those elements behave when they're selected. Depending on which context you received a selector function from, it will either select from the global scope or the repeated item scope. ## Service plugin [Service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md) allow you to extend and customize your site's functionality. You can use [service plugins](https://www.wix.com/velo/reference/spis/getting-started) to inject your own custom logic into a part of an existing, out-of-the-box app's flow. You can also use service plugins to integrate with 3rd-party services so that your Wix site and the external services can communicate seamlessly. ## Sidebar The [Code sidebar](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/about-development-workspaces.md) shows all of the files that make up your site, including pages, popups, files, routers, and database collections. Working in the sidebar, you can perform a variety of actions that affect your site. ## Sync (copy/overwrite) [Sync](https://support.wix.com/en/article/cms-accessing-and-syncing-your-sandbox-and-live-collections) allows you to move your data between your [sandbox](https://support.wix.com/en/article/managing-your-sandbox-and-live-data) and [live](https://support.wix.com/en/article/cms-accessing-and-syncing-your-sandbox-and-live-collections#accessing-and-syncing-sandbox-and-live-collections) databases. You can copy selected or all items in a collection from a sandbox collection to the live version of that collection. You can also overwrite a sandbox collection with all the data from the live version, or overwrite all your sandbox collections with the data from their live versions. You can only initiate sync operations from the sandbox database. Sandbox and live aren't always enabled for your collections. Learn more [about sandbox and live collections](https://support.wix.com/en/article/about-sandbox-and-live-collections-and-syncing). ## Test site A [test site](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md) is a version of your site released to a percentage of visitors so you can validate changes before publishing. Manage test sites in the [Release Manager](https://support.wix.com/en/article/accessing-the-release-manager). ## User input element [User input elements](https://support.wix.com/en/article/cms-about-user-input-elements) capture and store user data in a collection. Connect them to a [dataset](https://support.wix.com/en/article/cms-about-datasets) with [mode](https://support.wix.com/en/article/working-with-dataset-modes-and-collection-permissions) set to Write-only or Read & Write. ## Validation Validate data entered via [user input elements](https://support.wix.com/en/article/cms-about-user-input-elements) against your criteria, using either the [settings panel](https://support.wix.com/en/article/validating-user-input-with-the-settings-panel) or [code](https://support.wix.com/en/article/validating-user-input-with-code). ## Web module [Web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) enable you to write functions in the backend and call them from your frontend code. You import functions from backend web modules into your frontend, and the functions execute on the backend. Wix handles all the all the backend-frontend communication required to enable this access. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Quick Start ## Article: Quick Start ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/quick-start/quick-start.md ## Article Content: # Quick Start With Wix you can easily extend a site's functionality using code. This tutorial walks you through creating a simple button that changes text when clicked. Want to see it in action? Check out our [Hello World example](https://dev.wix.com/docs/coding-examples/getting-started/hello-world/hello-world.md). In this tutorial, you'll: - [Enable coding in Wix Studio.](#step-1--enable-coding) - [Add elements and set IDs.](#step-2--add-elements-and-set-ids) - [Set initial text with `onReady()`.](#step-3--set-initial-text-with-onready) - [Add an event handler.](#step-4--add-an-event-handler) - [Preview and test your code.](#step-5--preview-your-code) ## Before you begin Sign up for a [Wix Studio account](https://www.wix.com/studio) and log in. ## Step 1 | Enable coding In the left sidebar, click ![Code icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9c7667c9a288fa900e86664602c5a94e.png) and then **Start Coding**. ![Wix Studio start coding](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7bbc652d39e238e85896ddc1798c9823.png) This enables the built-in code editor at the bottom of your page and gives you access to the site's code files, databases, and other developer tools. ## Step 2 | Add elements and set IDs Add a text element and button to your page, then set meaningful IDs: 1. Add a **Text** element and set its ID to `helloText` 2. Add a **Button** element and set its ID to `clickMeButton` > **Tip:** Always use descriptive IDs for elements you'll reference in code. To access these elements in your code, you'll use Wix's `$w` selector: ```js // Select elements by ID $w("#clickMeButton"); // Button $w("#helloText"); // Text element ``` > **Note:** You can't use standard DOM APIs or libraries like jQuery to access Wix page elements. ## Step 3 | Set initial text with `onReady()` Wix provides an `onReady()` handler that runs when page elements finish loading. Use it to set the initial text of your element: ```js $w.onReady(function () { $w("#helloText").text = "Hello There"; }); ``` ## Step 4 | Add an event handler Now add a click event handler to the button that changes the text: ```js $w("#clickMeButton").onClick(() => { $w("#helloText").text = "Hello Wix!"; }); ``` Note that you can add the event handler directly in the `onReady()` handler, or outside it if you prefer. ## Step 5 | Preview your code Click **Run** in the code editor or the Preview icon ![Preview icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/396535a2065ce724807326c1f5071d43.png) to test your code. The developer console shows at the bottom for debugging. ## Next steps Now that you understand Wix's core patterns, explore these resources to learn more: - **[Coding Examples](https://dev.wix.com/docs/coding-examples.md)**: Interactive examples with working code previews - **[$w API Reference](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md)**: Complete guide to the selector API - **[Backend code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md)**: backend functions and web modules - **[Data API](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-the-data-api.md)**: Database operations and CMS integration - **[npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md)**: Adding 3rd-party libraries --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Block Off Time in the Bookings Calendar ## Article: Tutorial | Block Off Time in the Bookings Calendar ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/bookings/tutorial-block-off-time-in-the-bookings-calendar.md ## Article Content: # Tutorial | Block Off Time in the Bookings Calendar Using the [Calendar APIs](https://dev.wix.com/docs/api-reference/business-management/calendar/introduction.md), you can programmatically block time slots in your staff's Bookings calendars. This tutorial demonstrates how to build an interface that allows Wix users to efficiently manage staff availability by creating blocked time sessions for individual staff members or resources. This functionality is useful for scenarios such as staff vacation and time off, equipment maintenance, or training sessions. By the end of this tutorial, you'll have a resource availability management interface that allows Wix users to: - Select specific staff members or resources to manage. - Block single time slots on staff calendars. - Create recurring blocked time patterns.
**Caution:** The interface created in this tutorial should only be accessible in a protected area, not exposed to regular site visitors. Consider implementing this functionality in a [dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md) or on a [members-only page](https://support.wix.com/en/article/wix-editor-creating-members-only-pages) with appropriate permissions.
> The code in this article was written using the following module versions: > > - @wix/bookings (v1.0.1017) > - @wix/calendar (v1.0.148) > - @wix/web-methods (v1.0.11) > - @wix/essentials (v0.1.28) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). Use the following steps to build the functionality: 1. [Set up page elements.](#step-1--set-up-page-elements) 2. [Add backend code.](#step-2--add-backend-code) 3. [Add page code.](#step-3--add-page-code) ## Before you begin It's important to note the following points before doing this tutorial: - The site must have the [Wix Bookings app](https://www.wix.com/app-market/wix-bookings) installed. - The site must have at least 1 staff member or resource set up in the Bookings dashboard. - For recurring blocked time, only intervals of 1 or more weeks are supported. ## Step 1 | Add elements to your page This step creates an interface for Wix users to set up blocked time events. At the end of this step, you'll have a functional page with all the necessary input elements for blocking time slots, including options for recurring patterns. The interface should look like this: ![Block off time interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/33eb39b5c8d5ae5e3fec707576dee046.png) To create the interface: Add the following elements to your page: - A [dropdown](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/introduction.md) for selecting staff members/resources. Set the dropdown ID to `resourcesDropdown`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the start date. Set the date picker ID to `startDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the start time. Set the time picker ID to `startTime`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the end date. Set the date picker ID to `endDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the end time. Set the time picker ID to `endTime`. - A [checkbox](https://dev.wix.com/docs/velo/velo-only-apis/$w/checkbox/introduction.md) to enable recurring blocked time. Set the checkbox ID to `recurrenceCheckbox`. - A [container](https://dev.wix.com/docs/velo/velo-only-apis/$w/container/introduction.md) to hold recurring-specific elements. Set the container ID to `recurrenceContainer`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the cutoff date of recurrence. Set the date picker ID to `untilDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the cutoff time of recurrence. Set the time picker ID to `untilTime`. - A [dropdown](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/introduction.md) for selecting the recurrence interval, in weeks. Set the dropdown ID to `intervalDropdown`. - A [button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) for creating the blocked time event. Set the button ID to `button`. 2. Place `untilDate`, `untilTime`, and `intervalDropdown` inside `recurrenceContainer`. 3. In the **Properties & Events** panel, set `recurrenceContainer` to **Collapsed** by default. 4. Select `intervalDropdown`, and then select **Manage Choices** to populate the dropdown with options. Each option represents a number of weeks to set as the interval between recurring events. The panel should look like this: ![Choices panel, showing the options: 1:1, 2:2, 3:3, 4:4](https://wixmp-833713b177cebf373f611808.wixmp.com/images/00f231e02cc8bf4d1325daa363553886.png) ## Step 2 | Add backend code This step creates a backend code file with methods that call Wix APIs. At the end of this step, you'll have 2 backend methods: - A method that retrieves Bookings resources and responds with a list of options for a dropdown element. - A method that creates a blocked off event in the Bookings calendar. To add backend code: 1. Create a new backend file called `blockedOffTime.web.js` in your backend folder. 2. Add the required imports: ```javascript import { webMethod, Permissions } from "@wix/web-methods"; import { auth } from "@wix/essentials"; import { resources } from "@wix/bookings"; import { events } from "@wix/calendar"; ``` 3. Define a [web method](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction?apiView=SDK.md) that gets a list of Bookings resources by calling `queryResources()`, and returns a mapping of the resource into objects with the following fields: - `label`: The name of the resource, to display as a dropdown option. - `value`: A resource ID, to provide as a value for the dropdown option. ```javascript export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResources({}); const mappedResults = results.items.map((resource) => { return { label: resource.name, value: resource.eventsSchedule.scheduleId, }; }); return mappedResults; } catch (error) { console.error(error); return error; } }); ``` 4. Create a second web method that receives an `event` object. The method defines an [elevated](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-elevated-permissions?apiView=SDK.md) call to the Calendar API's `createEvent()` method, and makes the call with the specified `event` object. > **Note:** > Elevated permissions are unnecessary if the implementation is in a dashboard page. ```javascript export const createBlockedOffTimeEvent = webMethod( Permissions.Anyone, async (event) => { try { const elevatedCreateEvent = auth.elevate(events.createEvent); return elevatedCreateEvent(event); } catch (error) { return error; } } ); ``` ## Complete backend code ```javascript // blockedOffTime.web.js import { webMethod, Permissions } from "@wix/web-methods"; import { auth } from "@wix/essentials"; import { resources } from "@wix/bookings"; import { events } from "@wix/calendar"; export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResources({}); const mappedResults = results.items.map((resource) => { return { label: resource.name, value: resource.eventsSchedule.scheduleId, }; }); return mappedResults; } catch (error) { console.error(error); return error; } }); export const createBlockedOffTimeEvent = webMethod( Permissions.Anyone, async (event) => { try { const elevatedCreateEvent = auth.elevate(events.createEvent); return elevatedCreateEvent(event); } catch (error) { return error; } } ); ``` ## Step 3 | Add frontend code This step ties everything together. We use page code to define the behavior of some page elements and call our backend methods. At the end of this step, you'll have page code that: - Populates `resourcesDropdown` with options using our backend method. - Expands and collapses `recurrenceContainer` based on `recurrenceCheckbox`. - Calls the backend method `createBlockedOffTimeEvent` when the button is pressed. To add the following code to your page: 1. Import the methods you created in step 2: ```javascript import { getResourceList, createBlockedOffTimeEvent, } from "backend/blockedOffTime.web"; ``` 2. When the page loads, populate the resources dropdown, and set up an `onClick` event handler for the button and an `onChange` event handler for the checkbox. The next steps define these methods. ```javascript $w.onReady(function () { loadResourceDropdown(); $w("#recurrenceCheckbox").onChange(toggleRecurrenceContainer); $w("#button").onClick(blockOffTime); }); ``` 3. Define a method that calls `getResourceList()` from the backend and populates `resourcesDropdown` with the response. ```javascript async function loadResourceDropdown() { try { $w("#resourcesDropdown").options = await getResourceList(); } catch (error) { console.error("Failed to fetch resources.", error); } } ``` 4. Define a method that expands or collapses `recurrenceContainer` when `recurrenceCheckbox` becomes checked or unchecked. ```javascript function toggleRecurrenceContainer() { if ($w("#recurrenceCheckbox").checked) { $w("#recurrenceContainer").expand(); } else { $w("#recurrenceContainer").collapse(); } } ``` 5. Define a helper method that formats a date and a time into a single string. The Events API requires the following format for `localDate` fields: `YYYY-MM-DDTHH:mm:ss`. The `'en-CA'` locale provides the correct format for the date. ```javascript function formatLocalDate(date, time) { return date.toLocaleDateString("en-CA") + "T" + time; } ``` 6. Define a method to block off time in the calendar. First, get data from page elements, format it to match the requirements of the [Calendar API](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/create-event?apiView=SDK.md), and then create an `event` object. ```javascript async function blockOffTime() { const startTime = formatLocalDate( $w("#startDate").value, $w("#startTime").value ); const endTime = formatLocalDate($w("#endDate").value, $w("#endTime").value); try { let event = { scheduleId: $w("#resourcesDropdown").value, start: { localDate: startTime }, end: { localDate: endTime }, }; // Put the code from the next steps here. } catch (error) { console.error("Event creation failed.", error); } } ``` 7. If `recurrenceCheckbox` is checked, add recurrence data to the `event` object. The `'en-CA'` locale and `{ weekday: 'long' }` provide the chosen date's weekday in English. ```javascript if ($w("#recurrenceCheckbox").checked) { const recurrenceDay = $w("#startDate") .value.toLocaleDateString("en-CA", { weekday: "long" }) .toUpperCase(); const untilTime = formatLocalDate( $w("#untilDate").value, $w("#untilTime").value ); event = { ...event, recurrenceRule: { frequency: "WEEKLY", interval: $w("#intervalDropdown").value, days: [recurrenceDay], until: { localDate: untilTime }, }, }; } // Put the code from the next step here. ``` 8. Finish the method by calling the `createBlockedOffTimeEvent()` method from the backend, specifying the constructed `event` object. ```javascript createBlockedOffTimeEvent(event); ``` You've successfully created an interface that allows Wix users to block off time slots in staff calendars, including support for both single time blocks and recurring patterns. ## Complete page code ```javascript import { getResourceList, createBlockedOffTimeEvent, } from "backend/blockedOffTime.web"; $w.onReady(function () { loadResourceDropdown(); $w("#recurrenceCheckbox").onChange(toggleRecurrenceContainer); $w("#button").onClick(blockOffTime); }); async function loadResourceDropdown() { try { $w("#resourcesDropdown").options = await getResourceList(); } catch (error) { console.error("Failed to fetch resources.", error); } } function toggleRecurrenceContainer() { if ($w("#recurrenceCheckbox").checked) { $w("#recurrenceContainer").expand(); } else { $w("#recurrenceContainer").collapse(); } } function formatLocalDate(date, time) { return date.toLocaleDateString("en-CA") + "T" + time; } async function blockOffTime() { const startTime = formatLocalDate( $w("#startDate").value, $w("#startTime").value ); const endTime = formatLocalDate($w("#endDate").value, $w("#endTime").value); try { let event = { scheduleId: $w("#resourcesDropdown").value, start: { localDate: startTime }, end: { localDate: endTime }, }; if ($w("#recurrenceCheckbox").checked) { const recurrenceDay = $w("#startDate") .value.toLocaleDateString("en-CA", { weekday: "long" }) .toUpperCase(); const untilTime = formatLocalDate( $w("#untilDate").value, $w("#untilTime").value ); event = { ...event, recurrenceRule: { frequency: "WEEKLY", interval: $w("#intervalDropdown").value, days: [recurrenceDay], until: { localDate: untilTime }, }, }; } createBlockedOffTimeEvent(event); } catch (error) { console.error("Session creation failed.", error); } } ``` ## See also - [Recurring Events](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/recurring-events.md) - [Calendar Events API](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/introduction.md) - [Bookings API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Custom Bookings Experience for Appointment Services ## Article: Tutorial | Create a Custom Bookings Experience for Appointment Services ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/bookings/tutorial-create-a-custom-bookings-experience-for-appointment-services.md ## Article Content: # Tutorial | Create a Custom Bookings Experience for Appointment Services Using the JavaScript SDK [Bookings APIs](https://dev.wix.com/docs/sdk/backend-modules/bookings/introduction.md), you can create a custom booking experience for the [appointment-based services](https://dev.wix.com/docs/sdk/backend-modules/bookings/services/about-service-types.md) you offer on your site. This tutorial demonstrates how to build a complete booking flow that integrates with [Wix eCommerce](https://dev.wix.com/docs/sdk/backend-modules/ecom/introduction.md) for payment processing.
This tutorial covers appointment-based services only. For class and course services, the availability checking and booking processes are different and require separate implementation approaches.
## What you'll build By the end of this tutorial, you'll have a fully functional booking flow that allows site visitors to: - Browse available appointment services. - Select an appointment time slot. - Enter their contact information. - Complete payment through Wix eCommerce. To build the custom bookings experience, follow these steps: 1. [Set up the page elements and structure](#step-1--set-up-the-page-elements-and-structure). 2. [Build the backend logic to fetch available time slots](#step-2--build-the-backend-logic-to-fetch-available-time-slots). 3. [Build the backend logic to create bookings and process payments](#step-3--build-the-backend-logic-to-create-bookings-and-process-payments). 4. [Create the basic booking flow framework](#step-4--create-the-basic-booking-flow-framework). 5. [Enable customers to browse and select services](#step-5--enable-customers-to-browse-and-select-services). 6. [Display available appointment times to customers](#step-6--display-available-appointment-times-to-customers). 7. [Allow customers to choose their preferred time slot](#step-7--allow-customers-to-choose-their-preferred-time-slot). 8. [Process the booking and redirect to Wix eCommerce checkout](#step-8--process-the-booking-and-redirect-to-wix-ecommerce-checkout).
The code in this article was written using the following module versions: - @wix/site-ecom (v1.10.0) - @wix/bookings (v1.0.944) - @wix/ecom (v1.0.1243) - @wix/web-methods (v1.0.0) Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md).
## Before you begin It's important to note the following points before doing this tutorial: - [Set up your services](https://support.wix.com/en/wix-bookings/setting-up-wix-bookings) using the Bookings app. - This tutorial covers [appointment-based services](https://dev.wix.com/docs/sdk/backend-modules/bookings/services/about-service-types.md) only. Class and course services have different availability patterns and booking flows that aren't covered in this tutorial. - For paid bookings, [set up payment processing](https://support.wix.com/en/article/about-accepting-payments) before using the Bookings APIs. ## Step 1 | Add elements to your page This step sets up the page elements needed for the booking flow. At the end of this step, you'll have a complete page structure ready for implementing the booking functionality. To set up the page structure: 1. Add a dataset and connect it to the **Bookings/Services** collection. Learn more about [Adding a Dataset](https://support.wix.com/en/article/cms-formerly-content-manager-adding-and-setting-up-a-dataset). 2. Add the following elements to your page for services display and connect the repeater elements to their respective fields from the **Bookings/Services** collection through the dataset: | Type | ID | Connected to collection/field | | -------- | --------------- | ----------------------------- | | Section | serviceSection | - | | Repeater | serviceRepeater | Bookings/Services | | Text | titleText | Service Name (Text) | | Image | serviceImage | Service Image (Image) | | Text | descriptionText | Service Description (Text) | | Text | priceText | Price Summary (Text) | | Button | bookButton | - | The layout should look like this: ![Service section layout](https://wixmp-833713b177cebf373f611808.wixmp.com/images/74bce1b41a1679a562fd270c3676e4d8.png) 3. Add the following page elements for time slots display. | Type | ID | Purpose | | -------- | ------------ | ---------------------------- | | Section | slotSection | Contains slot selection UI | | Repeater | slotRepeater | Display available time slots | | Text | dateText | Show slot date | | Text | timeText | Show slot time | | Text | durationText | Show slot duration | | Button | slotButton | Select time slot | The layout should look like this: ![Time slots section layout with element IDs](https://wixmp-833713b177cebf373f611808.wixmp.com/images/175ab69ef9bdd4eadbe2d05707a8eb41.png) 4. Add the following page elements for the booking form. | Type | ID | Purpose | | ------- | -------------- | ------------------------------ | | Section | formSection | Contains customer input fields | | Input | firstNameInput | Customer first name | | Input | lastNameInput | Customer last name | | Input | emailInput | Customer email | | Input | phoneInput | Customer phone number | | Button | checkoutButton | Add booking to cart | The layout should look like this: ![Booking form section layout with element IDs](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6a74ba8a32f852ec91df771b2bd00e4b.png) ## Step 2 | Add backend code This step creates the backend web module for checking slot availability. At the end of this step, you'll have a backend web module that handles slot availability: fetching a list of available slots and verifying that a specific slot is still bookable right before creating a booking.
The availability checking methods in this step work with appointment-based services. Class and course services use different availability patterns and require [different API methods](https://dev.wix.com/docs/sdk/backend-modules/bookings/time-slots/introduction.md).
1. Create a new backend file called `bookings-availability.web.js` in your backend folder. 2. Add the required imports. ```javascript /***************************************** * Backend code - bookings-availability.web.js * ****************************************/ import { webMethod, Permissions } from "@wix/web-methods"; import { availabilityTimeSlots } from "@wix/bookings"; ``` 3. Add a [web method](https://dev.wix.com/docs/sdk/core-modules/web-methods/web-method.md) to list available slots. It calls [`listAvailabilityTimeSlots()`](https://dev.wix.com/docs/sdk/backend-modules/bookings/time-slots/availability-time-slots/list-availability-time-slots.md) to retrieve available booking slots for a service. The frontend code passes the required parameters: `serviceId`, `fromDate`, `toDate`, `timeSlotsPerDay`, and `timeZone`: ```javascript // Get available time slots for a service using the Time Slots API export const listAvailableSlots = webMethod( Permissions.Anyone, async (serviceId, fromDate, toDate, timeSlotsPerDay, timeZone) => { try { const response = await availabilityTimeSlots.listAvailabilityTimeSlots( { serviceId: serviceId, fromLocalDate: fromDate, toLocalDate: toDate, timeSlotsPerDay: timeSlotsPerDay, timeZone: timeZone, } ); return { success: true, timeSlots: response.timeSlots || [], totalSlots: response.timeSlots ? response.timeSlots.length : 0, }; } catch (error) { console.error("Error fetching availability:", error); return { success: false, error: error.message || "Unknown error occurred", timeSlots: [], totalSlots: 0, }; } } ); ``` 4. Add the second method to verify slot availability. This prevents double-booking by checking if a slot is still available right before creating the booking. It calls [`getAvailabilityTimeSlot()`](https://dev.wix.com/docs/sdk/backend-modules/bookings/time-slots/availability-time-slots/get-availability-time-slot.md) to verify a specific time slot is still bookable. The method checks the slot's `bookable` property to determine if it can still accept bookings. The frontend code passes the required parameters: `serviceId`, `localStartDate`, `localEndDate`, `timeZone`, and `location`. ```javascript // Verify a specific time slot is still available export const verifyTimeSlotAvailability = webMethod( Permissions.Anyone, async (serviceId, localStartDate, localEndDate, timeZone, location) => { // Validate required parameters if (!serviceId || !localStartDate || !localEndDate) { console.error("Missing required parameters for verification"); return { success: false, error: "Missing required parameters", isAvailable: false, }; } try { const response = await availabilityTimeSlots.getAvailabilityTimeSlot( serviceId, localStartDate, localEndDate, timeZone, location ); return { success: true, timeSlot: response.timeSlot, isAvailable: response.timeSlot && response.timeSlot.bookable, }; } catch (error) { console.error("Error verifying slot:", error); return { success: false, error: error.message || "Slot verification failed", isAvailable: false, }; } } ); ``` ## Step 3 | Build booking and payment logic This step creates the backend web module for booking creation and cart integration. The backend web module handles a 2-step process: first creating a booking and then adding it to the eCommerce cart for payment processing. 1. Create a new backend file called `bookings-checkout.web.js` in your backend folder. 2. Add the required imports. ```javascript /***************************************** * Backend code - bookings-checkout.web.js * ****************************************/ import { Permissions, webMethod } from "@wix/web-methods"; import { currentCart } from "@wix/ecom"; import { bookings } from "@wix/bookings"; ``` 3. Add a helper function to prepare booking data. ```javascript // Helper function to prepare booking data from the frontend options function prepareBookingData(bookingOptions) { return { bookedEntity: { slot: bookingOptions.bookedEntity.slot, }, contactDetails: { firstName: bookingOptions.contactDetails.firstName, lastName: bookingOptions.contactDetails.lastName, email: bookingOptions.contactDetails.email, phone: bookingOptions.contactDetails.phone, }, numberOfParticipants: bookingOptions.numberOfSpots || 1, }; } ``` 4. Add a helper function to create the booking. This function calls [`createBooking()`](https://dev.wix.com/docs/sdk/backend-modules/bookings/bookings/create-booking.md) and handles any errors that occur during booking creation. ```javascript // Helper function to create a booking and handle errors async function createBookingWithErrorHandling(bookingData) { try { const createdBookingResponse = await bookings.createBooking(bookingData); // Validate booking creation before proceeding if (!createdBookingResponse || !createdBookingResponse.booking._id) { console.error("Booking creation returned invalid result"); return { success: false, error: "Booking creation failed - no booking ID returned", step: "booking_validation", }; } return { success: true, booking: createdBookingResponse, }; } catch (bookingError) { console.error("Failed to create booking:", bookingError); return { success: false, error: "Failed to create booking: " + (bookingError.message || "Unknown booking error"), step: "booking_creation", details: bookingError.details || null, }; } } ``` 5. Add a helper function to add the booking to the cart. This function uses [`addToCurrentCart()`](https://dev.wix.com/docs/sdk/backend-modules/ecom/current-cart/add-to-current-cart.md) to add the created booking to the eCommerce cart by specifying the Wix Bookings app ID and the booking ID. ```javascript // Helper function to add booking to cart and handle errors async function addBookingToCartWithErrorHandling(bookingId) { try { const cartOptions = { lineItems: [ { catalogReference: { appId: "13d21c63-b5ec-5912-8397-c3a5ddb27a97", // Wix Bookings app ID catalogItemId: bookingId, }, quantity: 1, }, ], }; const updatedCurrentCart = await currentCart.addToCurrentCart( cartOptions ); return { success: true, cart: updatedCurrentCart, }; } catch (cartError) { console.error("Error adding booking to cart:", cartError); return { success: false, error: "Failed to add booking to cart: " + (cartError.message || "Unknown cart error"), step: "cart_addition", details: cartError.details || null, }; } } ``` 6. Implement the main web method that coordinates the helper functions. ```javascript // Main web method that coordinates booking creation and cart addition export const addBookingToCart = webMethod( Permissions.Anyone, async (bookingOptions) => { // Step 1: Prepare the booking data const bookingData = prepareBookingData(bookingOptions); // Step 2: Create the booking const bookingResult = await createBookingWithErrorHandling(bookingData); if (!bookingResult.success) { return bookingResult; // Return error from booking creation } // Step 3: Add booking to cart const cartResult = await addBookingToCartWithErrorHandling( bookingResult.booking.booking._id ); if (!cartResult.success) { // In a production site, handle this partial success scenario carefully. // The booking exists but is unpaid - consider canceling it or notifying site owners for follow-up. console.log( "Booking created but cart addition failed. Booking ID:", bookingResult.booking.booking._id ); return { ...cartResult, booking: bookingResult.booking, // Include booking info even if cart failed }; } // Step 4: Return success response return { booking: bookingResult.booking, cart: cartResult.cart, success: true, message: "Booking created and added to cart successfully", }; } ); ``` ## Step 4 | Add frontend code This step sets up the frontend code structure for the booking flow. You'll create the imports, utility functions, and basic UI setup needed for the booking functionality. At the end of this step, you'll have a page with form validation and checkout button handling, ready to integrate with the service and slot selection features. To set up the booking infrastructure: 1. Add the imports and global variables at the top of your page code. ```javascript import { ecom } from "@wix/site-ecom"; import { listAvailableSlots, verifyTimeSlotAvailability, } from "backend/bookings-availability.web.js"; import { addBookingToCart } from "backend/bookings-checkout.web.js"; // Global variables let availableSlots = []; let selectedSlot = null; let selectedServiceId = null; ``` 2. Add utility and helper functions. ```javascript // Optional: Utility method to format dates for API calls function toLocalISOString(date) { // Convert date to local time ISO string const offset = date.getTimezoneOffset(); // Adjust for timezone: getTimezoneOffset() returns minutes, so multiply by 60*1000 to get milliseconds // Subtract because getTimezoneOffset() returns positive values for timezones behind UTC const localDate = new Date(date.getTime() - offset * 60 * 1000); // Convert to ISO string and remove the 'Z' suffix // slice(0, -1) removes the last character ('Z') to indicate local time, not UTC return localDate.toISOString().slice(0, -1); } // Helper function to validate form input and slot selection function validateFormInput() { // Check if a slot has been selected if (!selectedSlot) { console.error("No slot selected or slot data missing"); return false; } // Validate form inputs and remove leading/trailing whitespace with trim() const firstName = $w("#firstNameInput").value.trim(); const lastName = $w("#lastNameInput").value.trim(); const email = $w("#emailInput").value.trim(); const phone = $w("#phoneInput").value.trim(); // Confirm all required input fields are filled if (!firstName || !lastName || !email) { // In a production site, consider displaying a message to the site visitor. // For example: $w("#errorMessage").text = "Please fill in all required fields"; console.log("Please fill in all required fields"); return false; } return true; } ``` 3. Set up the `$w.onReady()` function with complete functionality. ```javascript $w.onReady(function () { // Initially hide slot and form sections (service section visible by default) $w("#slotSection").hide(); $w("#formSection").hide(); // Add event listener for the checkout button $w("#checkoutButton").onClick(() => { // Validate form input and slot selection if (!validateFormInput()) { return; } // Process the booking (create booking and add to cart) processBooking(); }); }); ``` ## Step 5 | Enable customers to browse and select services This step implements the service selection user interface. At the end of this step, clicking a service's book button shows the slot selection section though slots don't load yet. To set up service selection: 1. Add the service repeater item ready function. This function handles when a service item is displayed in the repeater and sets up the book button click handler. ```javascript export function serviceRepeaterItemReady($item, itemData, index) { $item("#bookButton").onClick(async () => { selectedServiceId = itemData._id; if (selectedServiceId) { try { await loadAvailableSlots(selectedServiceId); // Show slotSection when bookButton is clicked $w("#slotSection").show(); } catch (error) { console.error("Error loading available slots:", error); // In a production site, consider displaying a message to site visitors via UI elements. // For example: $w("#serviceErrorMessage").text = "Unable to load available times. Please try again."; } } else { // In a production site, consider displaying a message to site visitors via UI elements. // For example: $w("#serviceErrorMessage").text = "Service unavailable. Please try again."; console.log("No service ID found"); } }); } ``` 2. Add the service repeater registration to your `$w.onReady()` function. ```javascript $w.onReady(function () { // ... existing code ... // Register the service repeater item ready method $w("#serviceRepeater").onItemReady(serviceRepeaterItemReady); }); ``` ## Step 6 | Display available appointment times to customers This step implements the actual slot loading functionality by replacing the stub function with a real implementation. At the end of this step, site visitors can view available time slots for their selected service. To load and display available slots: 1. Add the `loadAvailableSlots` function to your page code. When a site visitor selects a service by clicking the book button, this function fetches available time slots for that service. This example queries availability for the next 7 days and limits results to 1 slot per day. You can adjust the time range and slots per day to better suit your business needs and user experience requirements. ```javascript // Load available time slots for the selected service async function loadAvailableSlots(serviceId) { try { // Set up the date range and time zone parameters const today = new Date(); const endRange = new Date(); // Set query time range (the example uses 7 days ahead, you can customize this range as needed) endRange.setDate(today.getDate() + 7); // Limit to 1 slot per day - increase for more options let slotsPerDay = 1; // Use the customer's timezone or default to UTC // Note: UTC fallback may cause timezone mismatches for international customers let timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; // Call the backend method to fetch available slots const availability = await listAvailableSlots( serviceId, toLocalISOString(today), toLocalISOString(endRange), slotsPerDay, timeZone ); // Handle the response and call the slot population function if (availability && availability.timeSlots) { availableSlots = availability.timeSlots; populateSlotRepeater(); } else { // In a production site, consider showing a message to the site visitor. // For example: $w("#noSlotsMessage").show(); console.log("No available slots found"); $w("#slotRepeater").data = []; } } catch (error) { console.error("Error fetching slots:", error); $w("#slotRepeater").data = []; } } ``` ## Step 7 | Allow customers to choose their preferred time slot This step implements the slot selection functionality with helper functions for cleaner, more maintainable code. At the end of this step, site visitors can choose from available appointment times. To display and interact with time slots: 1. Add the `populateSlotRepeater` function to your page code.
[Repeaters](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/editor-elements/repeaters/about-repeater-item-templates.md) require each data item to have an `_id` field that doesn't contain underscores within its value. The raw slot data from the [`listAvailabilityTimeSlots()`](https://dev.wix.com/docs/sdk/backend-modules/bookings/time-slots/availability-time-slots/list-availability-time-slots.md) doesn't include an `id` field, so you must transform the data. For example, by using the `map()` method. Without this transformation, the repeater won't display any items while no error is logged.
```javascript // Populate the time slots repeater function populateSlotRepeater() { try { if (!availableSlots || availableSlots.length === 0) { // In a production site, consider showing a message to site visitors via UI elements. // For example: $w("#noSlotsMessage").text = "No available time slots. Please try different dates."; console.log("No slots to display"); $w("#slotRepeater").data = []; return; } // Show slot section $w("#slotSection").show(); // Transform slots for repeater compatibility // IMPORTANT: Repeaters require each item to have an _id field that doesn't contain underscores // The raw slot data from the API doesn't meet this requirement, so we must map it const repeaterData = availableSlots.map((slot, index) => { return { _id: `slot-${index}`, // Simple ID for repeater (no underscores in the value) fullSlot: slot, // Nest the original slot data here }; }); // Set the repeater data $w("#slotRepeater").data = repeaterData; } catch (error) { console.error("Error populating repeater:", error); } } ``` 2. Add a helper function to format slot display data. ```javascript // Helper function to format slot data for display function formatSlotForDisplay(slotData) { const startDate = new Date(slotData.localStartDate); const endDate = new Date(slotData.localEndDate); // OPTIONAL: Format date using US locale with abbreviated month (e.g., "Jul 1, 2025") // Customize this formatting to match your preferences const dateStr = startDate.toLocaleDateString("en-US", { month: "short", // Abbreviated month name (Jan, Feb, etc.) day: "numeric", // Day without leading zero year: "numeric", // Full year }); // OPTIONAL: Format time using US locale with 12-hour format (e.g., "2:00 PM") // You can use 24-hour format or other locale settings as needed const timeStr = startDate.toLocaleTimeString("en-US", { hour: "numeric", // Hour without leading zero minute: "2-digit", // Minutes with leading zero if needed hour12: true, // Use 12-hour format with AM/PM }); // OPTIONAL: Calculate and display slot duration // You might prefer to show this information differently const durationMinutes = Math.round( (endDate.getTime() - startDate.getTime()) / (1000 * 60) ); const durationStr = `${durationMinutes} min`; return { dateStr, timeStr, durationStr }; } ``` 3. Add a helper function to handle slot selection. ```javascript // Helper function to handle slot selection function handleSlotSelection(slotData) { // Store the selected slot for booking creation selectedSlot = slotData; // Show your custom booking form $w("#formSection").show(); } ``` 4. Implement the main slot repeater item ready method that uses the helper functions. ```javascript export function slotRepeaterItemReady($item, itemData, index) { try { // Access the slot data from the fullSlot property due to our data transformation const slotData = itemData.fullSlot; // Format the slot data for display const { dateStr, timeStr, durationStr } = formatSlotForDisplay(slotData); // Set the formatted values to the repeater item's text elements $item("#dateText").text = dateStr; $item("#timeText").text = timeStr; $item("#durationText").text = durationStr; // Add event listener for the slot button - this is the core functionality // When clicked, this stores the selected slot and shows the booking form $item("#slotButton").onClick(() => { handleSlotSelection(slotData); }); } catch (error) { console.error("Error in slot repeater itemReady:", error); } } ``` 5. Add the slot repeater registration to your `$w.onReady()` function. ```javascript $w.onReady(function () { // ... existing code ... // Register the slot repeater item ready method $w("#slotRepeater").onItemReady(slotRepeaterItemReady); }); ``` ## Step 8 | Process the booking and redirect to Wix eCommerce checkout This step implements the booking creation functionality with a comprehensive set of helper functions for better code organization. The frontend `processBooking()` function calls the backend web module `addBookingToCart()` (created in step 3) that handles both creating the booking and adding it to the eCommerce cart. At the end of this step, you create the booking and automatically redirect customers to the cart page to complete their purchase. To implement the booking process: 1. Add helper functions to collect form data and validate slot selection. ```javascript // Helper function to collect form data function getFormData() { // Get form values and remove leading/trailing whitespace with trim() const firstName = $w("#firstNameInput").value.trim(); const lastName = $w("#lastNameInput").value.trim(); const email = $w("#emailInput").value.trim(); const phone = $w("#phoneInput").value.trim(); return { firstName, lastName, email, phone }; } // Helper function to validate slot selection function validateSlotSelection() { // Check if selectedSlot and its properties exist if (!selectedSlot) { throw new Error("No slot selected or slot data missing"); } // Ensure we have the required data if (!selectedSlot.localStartDate || !selectedSlot.localEndDate) { throw new Error("Slot missing start or end date"); } } ``` 2. Add a helper function to transform slot data for booking creation. You must transform the `slot` object returned from `listAvailabilityTimeSlots()` before specifying it in `createBooking()`. The required transformations are: - **Rename date fields**: `localStartDate`/`localEndDate` → `startDate`/`endDate`. - **Select a resource**: Choose 1 resource from the `availableResources` array. - **Map location type**: Convert between different enum values. | From `listAvailabilityTimeSlots()` | To `createBooking()` | | ---------------------------------- | -------------------- | | `CUSTOMER` | `OWNER_CUSTOM` | | `CUSTOM` | `CUSTOM` | | `BUSINESS` | `OWNER_BUSINESS` | ```javascript // Helper function to transform slot data for booking creation function transformSlotForBooking(contactDetails) { // Map location type from time slots API to bookings API format const locationTypeMap = { CUSTOMER: "OWNER_CUSTOM", CUSTOM: "CUSTOM", BUSINESS: "OWNER_BUSINESS", }; // Transform slot data for booking creation return { bookedEntity: { slot: { ...selectedSlot, startDate: selectedSlot.localStartDate, endDate: selectedSlot.localEndDate, location: { ...selectedSlot.location, locationType: locationTypeMap[selectedSlot.location.locationType] || "OWNER_BUSINESS", }, resource: { ...selectedSlot.resource, // Select a resource, it doesn't have to be the first resource. id: selectedSlot.availableResources[0].resources[0]._id, }, }, }, contactDetails, numberOfSpots: 1, }; } ``` 3. Add a helper function to verify slot availability. ```javascript // Helper function to verify slot availability before booking async function verifySlotAvailability() { // Use the customer's timezone or default to UTC // Note: UTC fallback may cause timezone mismatches for international customers const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; // Verify slot is still available const slotVerification = await verifyTimeSlotAvailability( selectedServiceId, selectedSlot.localStartDate, selectedSlot.localEndDate, timeZone, selectedSlot.location ); if (!slotVerification.success) { throw new Error( `Error verifying slot availability: ${slotVerification.error}` ); } if (!slotVerification.isAvailable) { throw new Error("Slot is no longer available for booking"); } } ``` 4. Implement the main `processBooking` function that coordinates the helper functions. This function orchestrates the entire booking process using the helper functions in a clear 6-step workflow. ```javascript // Process booking: validate, create booking, and add to cart async function processBooking() { try { // Step 1: Validate slot selection validateSlotSelection(); // Step 2: Collect form data const contactDetails = getFormData(); // Step 3: Transform slot data for booking const bookingOptions = transformSlotForBooking(contactDetails); // Step 4: Verify slot is still available await verifySlotAvailability(); // Step 5: Create the booking and add it to the cart const cartResult = await addBookingToCart(bookingOptions); // Step 6: Navigate to cart page for checkout await ecom.navigateToCartPage(); } catch (error) { console.error("Error processing booking:", error); // In a production site, consider displaying an error message to site visitors via UI elements. // For example: $w("#errorMessage").text = "Error processing booking. Please try again."; console.log("Error processing booking. Please try again."); } } ``` ## What happens next After the booking is added to the cart and the customer is redirected, they continue through the standard Wix eCommerce checkout flow: 1. **Cart page**: Site visitors review their booking details and continue to checkout. 2. **Checkout page**: Site visitors enter payment information and billing details. 3. **Confirmation**: Site visitors receive booking confirmation after successful payment. Congratulations. You've successfully created a custom bookings experience that integrates with Wix eCommerce for payment processing. You can continue building upon this example by adding features like booking confirmations, customer notifications, or apply what you learned in this tutorial to create entirely new booking workflows. ## Complete implementation code
Page code (frontend) ```javascript import { ecom } from "@wix/site-ecom"; import { listAvailableSlots, verifyTimeSlotAvailability, } from "backend/bookings-availability.web.js"; import { addBookingToCart } from "backend/bookings-checkout.web.js"; // Global variables let availableSlots = []; let selectedSlot = null; let selectedServiceId = null; function toLocalISOString(date) { // Convert date to local time ISO string const offset = date.getTimezoneOffset(); // Adjust for timezone: getTimezoneOffset() returns minutes, so multiply by 60*1000 to get milliseconds // Subtract because getTimezoneOffset() returns positive values for timezones behind UTC const localDate = new Date(date.getTime() - offset * 60 * 1000); // Convert to ISO string and remove the 'Z' suffix // slice(0, -1) removes the last character ('Z') to indicate local time, not UTC return localDate.toISOString().slice(0, -1); } // Load available time slots for the selected service async function loadAvailableSlots(serviceId) { try { // Set up the date range and time zone parameters const today = new Date(); const endRange = new Date(); // Set query time range (the example uses 7 days ahead, you can customize this range as needed) endRange.setDate(today.getDate() + 7); // Limit to 1 slot per day - increase for more options let slotsPerDay = 1; // Use the customer's timezone or default to UTC // Note: UTC fallback may cause timezone mismatches for international customers let timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; // Call the backend method to fetch available slots const availability = await listAvailableSlots( serviceId, toLocalISOString(today), toLocalISOString(endRange), slotsPerDay, timeZone ); // Handle the response and call the slot population function if (availability && availability.timeSlots) { availableSlots = availability.timeSlots; populateSlotRepeater(); } else { console.log("No available slots found"); // In a production site, consider showing a message to the site visitor. // For example: $w("#noSlotsMessage").show(); $w("#slotRepeater").data = []; } } catch (error) { console.error("Error fetching slots:", error); $w("#slotRepeater").data = []; } } // Populate the time slots repeater function populateSlotRepeater() { try { if (!availableSlots || availableSlots.length === 0) { $w("#slotRepeater").data = []; return; } // Show slot section $w("#slotSection").show(); // Transform slots for repeater compatibility // IMPORTANT: Repeaters require each item to have an _id field that doesn't contain underscores // The raw slot data from the API doesn't meet this requirement, so we must map it const repeaterData = availableSlots.map((slot, index) => { return { _id: `slot-${index}`, // Simple ID for repeater (no underscores in the value) fullSlot: slot, // Nest the original slot data here }; }); // Set the repeater data $w("#slotRepeater").data = repeaterData; } catch (error) { console.error("Error populating repeater:", error); } } // Helper function to format slot data for display function formatSlotForDisplay(slotData) { const startDate = new Date(slotData.localStartDate); const endDate = new Date(slotData.localEndDate); // OPTIONAL: Format date using US locale with abbreviated month (e.g., "Jul 1, 2025") // Customize this formatting to match your preferences const dateStr = startDate.toLocaleDateString("en-US", { month: "short", // Abbreviated month name (Jan, Feb, etc.) day: "numeric", // Day without leading zero year: "numeric", // Full year }); // OPTIONAL: Format time using US locale with 12-hour format (e.g., "2:00 PM") // You can use 24-hour format or other locale settings as needed const timeStr = startDate.toLocaleTimeString("en-US", { hour: "numeric", // Hour without leading zero minute: "2-digit", // Minutes with leading zero if needed hour12: true, // Use 12-hour format with AM/PM }); // OPTIONAL: Calculate and display slot duration // You might prefer to show this information differently const durationMinutes = Math.round( (endDate.getTime() - startDate.getTime()) / (1000 * 60) ); const durationStr = `${durationMinutes} min`; return { dateStr, timeStr, durationStr }; } // Helper function to handle slot selection function handleSlotSelection(slotData) { // Store the selected slot for booking creation selectedSlot = slotData; // Show your custom booking form $w("#formSection").show(); } // Helper function to validate form input and slot selection function validateFormInput() { // Check if a slot has been selected if (!selectedSlot) { console.error("No slot selected or slot data missing"); return false; } // Validate form inputs and remove leading/trailing whitespace with trim() const firstName = $w("#firstNameInput").value.trim(); const lastName = $w("#lastNameInput").value.trim(); const email = $w("#emailInput").value.trim(); const phone = $w("#phoneInput").value.trim(); // Confirm all required input fields are filled if (!firstName || !lastName || !email) { // In a production site, consider displaying a message to the site visitor. // For example: $w("#errorMessage").text = "Please fill in all required fields"; console.log("Please fill in all required fields"); return false; } return true; } // Helper function to collect form data function getFormData() { // Get form values and remove leading/trailing whitespace with trim() const firstName = $w("#firstNameInput").value.trim(); const lastName = $w("#lastNameInput").value.trim(); const email = $w("#emailInput").value.trim(); const phone = $w("#phoneInput").value.trim(); return { firstName, lastName, email, phone }; } // Helper function to validate slot selection function validateSlotSelection() { // Check if selectedSlot and its properties exist if (!selectedSlot) { throw new Error("No slot selected or slot data missing"); } // Ensure we have the required data if (!selectedSlot.localStartDate || !selectedSlot.localEndDate) { throw new Error("Slot missing start or end date"); } } // Helper function to transform slot data for booking creation function transformSlotForBooking(contactDetails) { // Map location type from time slots API to bookings API format const locationTypeMap = { CUSTOMER: "OWNER_CUSTOM", CUSTOM: "CUSTOM", BUSINESS: "OWNER_BUSINESS", }; // Transform slot data for booking creation return { bookedEntity: { slot: { ...selectedSlot, startDate: selectedSlot.localStartDate, endDate: selectedSlot.localEndDate, location: { ...selectedSlot.location, locationType: locationTypeMap[selectedSlot.location.locationType] || "OWNER_BUSINESS", }, resource: { ...selectedSlot.resource, // Select a resource, it doesn't have to be the first resource. id: selectedSlot.availableResources[0].resources[0]._id, }, }, }, contactDetails, numberOfSpots: 1, }; } // Helper function to verify slot availability before booking async function verifySlotAvailability() { // Use the customer's timezone or default to UTC // Note: UTC fallback may cause timezone mismatches for international customers const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; // Verify slot is still available const slotVerification = await verifyTimeSlotAvailability( selectedServiceId, selectedSlot.localStartDate, selectedSlot.localEndDate, timeZone, selectedSlot.location ); if (!slotVerification.success) { throw new Error( `Error verifying slot availability: ${slotVerification.error}` ); } if (!slotVerification.isAvailable) { throw new Error("Slot is no longer available for booking"); } } // Process booking: validate, create booking, and add to cart async function processBooking() { try { // Step 1: Validate slot selection validateSlotSelection(); // Step 2: Collect form data const contactDetails = getFormData(); // Step 3: Transform slot data for booking const bookingOptions = transformSlotForBooking(contactDetails); // Step 4: Verify slot is still available await verifySlotAvailability(); // Step 5: Create the booking and add it to the cart const cartResult = await addBookingToCart(bookingOptions); // Step 6: Navigate to cart page for checkout await ecom.navigateToCartPage(); } catch (error) { console.error("Error processing booking:", error); // In a production site, consider displaying an error message to site visitors via UI elements. // For example: $w("#errorMessage").text = "Error processing booking. Please try again."; console.log("Error processing booking. Please try again."); } } export function serviceRepeaterItemReady($item, itemData, index) { $item("#bookButton").onClick(async () => { selectedServiceId = itemData._id; if (selectedServiceId) { try { await loadAvailableSlots(selectedServiceId); // Show slotSection when bookButton is clicked $w("#slotSection").show(); } catch (error) { console.error("Error loading available slots:", error); // In a production site, consider displaying a message to site visitors via UI elements. // For example: $w("#serviceErrorMessage").text = "Unable to load available times. Please try again."; } } else { console.log("No service ID found"); } }); } export function slotRepeaterItemReady($item, itemData, index) { try { // Access the slot data from the fullSlot property due to our data transformation const slotData = itemData.fullSlot; // Format the slot data for display const { dateStr, timeStr, durationStr } = formatSlotForDisplay(slotData); // Set the formatted values to the repeater item's text elements $item("#dateText").text = dateStr; $item("#timeText").text = timeStr; $item("#durationText").text = durationStr; // Add event listener for the slot button - this is the core functionality // When clicked, this stores the selected slot and shows the booking form $item("#slotButton").onClick(() => { handleSlotSelection(slotData); }); } catch (error) { console.error("Error in slot repeater itemReady:", error); } } $w.onReady(function () { // Initially hide slot and form sections (service section visible by default) $w("#slotSection").hide(); $w("#formSection").hide(); // Register the item ready methods for the slot and the service repeater $w("#slotRepeater").onItemReady(slotRepeaterItemReady); $w("#serviceRepeater").onItemReady(serviceRepeaterItemReady); // Add event listener for the checkout button $w("#checkoutButton").onClick(() => { // Validate form input and slot selection if (!validateFormInput()) { return; } // Process the booking (create booking and add to cart) processBooking(); }); }); ```
bookings-availability.web.js ```javascript /***************************************** * Backend code - bookings-availability.web.js * ****************************************/ import { webMethod, Permissions } from "@wix/web-methods"; import { availabilityTimeSlots } from "@wix/bookings"; /** * Get available time slots for a service using the Availability API */ export const listAvailableSlots = webMethod( Permissions.Anyone, async (serviceId, fromDate, toDate, timeSlotsPerDay, timeZone) => { try { const response = await availabilityTimeSlots.listAvailabilityTimeSlots({ serviceId: serviceId, fromLocalDate: fromDate, toLocalDate: toDate, timeSlotsPerDay: timeSlotsPerDay, timeZone: timeZone, }); return { success: true, timeSlots: response.timeSlots || [], totalSlots: response.timeSlots ? response.timeSlots.length : 0, }; } catch (error) { console.error("Error fetching availability:", error); return { success: false, error: error.message || "Unknown error occurred", timeSlots: [], totalSlots: 0, }; } } ); // Verify a specific time slot is still available export const verifyTimeSlotAvailability = webMethod( Permissions.Anyone, async (serviceId, localStartDate, localEndDate, timeZone, location) => { // Validate required parameters if (!serviceId || !localStartDate || !localEndDate) { console.error("Missing required parameters for verification"); return { success: false, error: "Missing required parameters", isAvailable: false, }; } try { const response = await availabilityTimeSlots.getAvailabilityTimeSlot( serviceId, localStartDate, localEndDate, timeZone, location ); return { success: true, timeSlot: response.timeSlot, isAvailable: response.timeSlot && response.timeSlot.bookable, }; } catch (error) { console.error("Error verifying slot:", error); return { success: false, error: error.message || "Slot verification failed", isAvailable: false, }; } } ); ```
bookings-checkout.web.js ```javascript /***************************************** * Backend code - bookings-checkout.web.js * ****************************************/ import { Permissions, webMethod } from "@wix/web-methods"; import { currentCart } from "@wix/ecom"; import { bookings } from "@wix/bookings"; // Helper function to prepare booking data from the frontend options function prepareBookingData(bookingOptions) { return { bookedEntity: { slot: bookingOptions.bookedEntity.slot, }, contactDetails: { firstName: bookingOptions.contactDetails.firstName, lastName: bookingOptions.contactDetails.lastName, email: bookingOptions.contactDetails.email, phone: bookingOptions.contactDetails.phone, }, numberOfParticipants: bookingOptions.numberOfSpots || 1, }; } // Helper function to create a booking and handle errors async function createBookingWithErrorHandling(bookingData) { try { const createdBookingResponse = await bookings.createBooking(bookingData); // Validate booking creation before proceeding if (!createdBookingResponse || !createdBookingResponse.booking._id) { console.error("Booking creation returned invalid result"); return { success: false, error: "Booking creation failed - no booking ID returned", step: "booking_validation", }; } return { success: true, booking: createdBookingResponse, }; } catch (bookingError) { console.error("Failed to create booking:", bookingError); return { success: false, error: "Failed to create booking: " + (bookingError.message || "Unknown booking error"), step: "booking_creation", details: bookingError.details || null, }; } } // Helper function to add booking to cart and handle errors async function addBookingToCartWithErrorHandling(bookingId) { try { const cartOptions = { lineItems: [ { catalogReference: { appId: "13d21c63-b5ec-5912-8397-c3a5ddb27a97", // Wix Bookings app ID catalogItemId: bookingId, }, quantity: 1, }, ], }; const updatedCurrentCart = await currentCart.addToCurrentCart(cartOptions); return { success: true, cart: updatedCurrentCart, }; } catch (cartError) { console.error("Error adding booking to cart:", cartError); return { success: false, error: "Failed to add booking to cart: " + (cartError.message || "Unknown cart error"), step: "cart_addition", details: cartError.details || null, }; } } // Main web method that coordinates booking creation and cart addition export const addBookingToCart = webMethod( Permissions.Anyone, async (bookingOptions) => { // Step 1: Prepare the booking data const bookingData = prepareBookingData(bookingOptions); // Step 2: Create the booking const bookingResult = await createBookingWithErrorHandling(bookingData); if (!bookingResult.success) { return bookingResult; // Return error from booking creation } // Step 3: Add booking to cart const cartResult = await addBookingToCartWithErrorHandling( bookingResult.booking.booking._id ); if (!cartResult.success) { // In a production site, handle this partial success scenario carefully. // The booking exists but is unpaid - consider canceling it or notifying site owners for follow-up. console.log( "Booking created but cart addition failed. Booking ID:", bookingResult.booking.booking._id ); return { ...cartResult, booking: bookingResult.booking, // Include booking info even if cart failed }; } // Step 4: Return success response return { booking: bookingResult.booking, cart: cartResult.cart, success: true, message: "Booking created and added to cart successfully", }; } ); ```
## See also - [Wix Bookings API Reference](https://dev.wix.com/docs/sdk/backend-modules/bookings/introduction.md) - [Wix Bookings architecture and data flow](https://dev.wix.com/docs/rest/business-solutions/bookings/architecture-and-data-flow.md) - [Wix eCommerce API Reference](https://dev.wix.com/docs/sdk/backend-modules/ecom/introduction.md) - [Wix eCommerce architecture and data flow](https://dev.wix.com/docs/rest/business-solutions/e-commerce/architecture-data-flow.md) - [Working with Web Modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Add Data Search and Filtering Functionality ## Article: Tutorial | Add Data Search and Filtering Functionality ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/data/tutorial-add-data-search-and-filtering-functionality.md ## Article Content: # Tutorial | Add Data Search and Filtering Functionality When you have a database collection with lots of content, site visitors need an easy way to find what they're looking for. You can add powerful search and filtering capabilities to your site, transforming static content displays into interactive, user-friendly experiences. This tutorial demonstrates how to build: - **Real-time text search** - Site visitors type in a search field and see results update instantly as they type. - **Dropdown filtering** - Site visitors select from predefined categories using a dropdown menu populated with unique values from your collection. You'll learn to query collections, handle user input, and create interfaces that enhance the user experience. We'll use the following steps to query a database collection and dynamically display matching results: 1. [Set up site elements](#step-1--add-elements-to-your-site). 1. [Query and display all content on page load](#step-2--query-and-display-all-content-on-page-load). 1. [Add search and filter functionality](#step-3--filter-the-repeater). > The code in this article was written using the following module versions: > > - @wix/data (v1.0.244) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Before you begin You need to note the following points before starting to code: - Create a [collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) on your site. ## Step 1 | Add elements to your page This step sets up the user interface elements needed for search and filtering functionality. 1. Add a [repeater](https://support.wix.com/en/article/studio-editor-adding-and-customizing-repeaters) to your page. Set the repeater ID to `articlesList`. Inside the repeater, add: - An image element. Set the image ID to `itemImage`. - Two text elements. Set the text IDs to `itemCountry` and `itemContent` 2. Add an input field for searching. Set the input ID to `inputTitle`. 3. Add a dropdown element. Set the dropdown ID to `inputContinent`. ## Step 2 | Query and display all content on page load This step retrieves all items from your collection and prepares them for display. When the page loads, you'll display all the items before any filtering is applied. To query a collection, you'll need to get the collection ID. The way you get the ID depends on the editor you're using: ::::tabs :::Wix-Studio 1. Click on the **CMS** tab in the Code panel. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::Wix-Editor 1. Click on the **Databases** tab in the Code sidebar. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::: Now that you have the collection ID, you can query your data: 1. Import the [`@wix/data`](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md) module, which provides the [`query()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md) method for retrieving collection data. Also, save the collection ID to the `collectionName` variable. ```javascript import { items } from "@wix/data"; const collectionName = "Articles"; ``` 2. Define a function that runs the query using the ID you retrieved above. Set the repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) property to your collection results: ```javascript async function loadAllResults() { const results = await items.query(collectionName).find(); $w("#articlesList").data = results.items; } ``` The query returns an array of [`items`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md), where each item represents an item from your collection. Collection [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) become object properties, so fields such as `image`, `country`, `content` appear as properties with the same names in each result object. 3. Use the [`onItemReady()`](https://www.wix.com/velo/reference/$w/repeater/onitemready) event handler to map your collection field values to specific repeater elements. This event runs automatically for each new element in the repeater's `.data` array. The handler receives an [`$item`](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) selector, which selects specific instances of repeated elements. In this example, `image`, `country`, and `content` are [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) from your database collection: ```javascript function initRepeater() { $w("#articlesList").onItemReady(($item, itemData) => { $item("#itemImage").src = itemData.image; $item("#itemCountry").text = itemData.country; $item("#itemContent").text = itemData.content; }); } ``` 4. Define a function to query your collection and populate the dropdown with all unique, non-empty `continent` values. Add a default option labeled "All" with the value `All`. This option allows site visitors to view all items, unfiltered. ```javascript async function initDropdown() { const queryResults = await items.query(collectionName).find(); const uniqueContinents = [ ...new Set(queryResults.items.map((item) => item.continent)), ] .filter(Boolean) .sort((a, b) => a.localeCompare(b)); $w("#inputContinent").options = [ { value: "All", label: "All" }, ...uniqueContinents.map((c) => ({ value: c, label: c })), ]; } ``` 5. Call the `initRepeater()`, `loadAllResults()`, and `initDropdown()` functions in the `$w.onReady()` handler: ```javascript $w.onReady(async function () { await initDropdown(); initRepeater(); await loadAllResults(); }); ``` ## Step 3 | Filter the repeater This step adds the interactive functionality that allows site visitors to search and filter the displayed content in real time. 1. Define a function called `buildFilterAndPopulateRepeater()`. It filters your repeater results by checking if the "country" field contains the input text. When a dropdown value is selected, this function filters the collection and updates the repeater. Also, set the repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) property to your collection results: ```javascript async function buildFilterAndPopulateRepeater(inputSearch, continent) { let dataQuery = items.query(collectionName); if (inputSearch) { dataQuery = dataQuery.contains("country", inputSearch); } if (continent && continent !== "All") { dataQuery = dataQuery.contains("continent", continent); } const results = await dataQuery.find(); $w("#articlesList").data = results.items; } ``` 2. For filtering by input, add an event handler that listens for any input changes in the search field. To prevent triggering too many queries, use a short delay after each keystroke. This technique, called [debouncing](https://developer.mozilla.org/en-US/docs/Glossary/Debounce), improves both performance and user experience by ensuring the search only runs after the site visitor has stopped typing for a specified period: ```javascript let debounceTimer; $w("#inputTitle").onInput(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }, 400); }); ``` 3. For filtering by dropdown, add an event handler that listens for any changes in the dropdown. ```javascript $w("#inputContinent").onChange(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }); ``` ## Complete code Here's the complete working code: ```javascript import { items } from "@wix/data"; const collectionName = "Articles"; let debounceTimer; $w.onReady(async function () { await initDropdown(); initRepeater(); await loadAllResults(); }); $w("#inputTitle").onInput(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }, 400); }); $w("#inputContinent").onChange(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }); async function initDropdown() { const queryResults = await items.query(collectionName).find(); const uniqueContinents = [ ...new Set(queryResults.items.map((item) => item.continent)), ] .filter(Boolean) .sort((a, b) => a.localeCompare(b)); $w("#inputContinent").options = [ { value: "All", label: "All" }, ...uniqueContinents.map((c) => ({ value: c, label: c })), ]; } async function loadAllResults() { const results = await items.query(collectionName).find(); $w("#articlesList").data = results.items.length; } async function buildFilterAndPopulateRepeater(inputSearch, continent) { let dataQuery = items.query(collectionName); if (inputSearch) { dataQuery = dataQuery.contains("country", inputSearch); } if (continent && continent !== "All") { dataQuery = dataQuery.contains("continent", continent); } const results = await dataQuery.find(); $w("#articlesList").data = results.items.length; } function initRepeater() { $w("#articlesList").onItemReady(($item, itemData) => { $item("#itemImage").src = itemData.image; $item("#itemCountry").text = itemData.country; $item("#itemContent").text = itemData.content; }); } ``` ## See also - [$item](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) - [Data Items API](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Add Navigation to a Dynamic Page ## Article: Tutorial | Add Navigation to a Dynamic Page ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/data/tutorial-add-navigation-to-a-dynamic-page.md ## Article Content: # Tutorial | Add Navigation to a Dynamic Page When you use dynamic pages, you often have a dynamic list page that displays multiple items and individual dynamic item pages for each item. If you want visitors to navigate between the individual items in a specific order, you need to ensure the navigation follows the sorted order of your dynamic list page. The built-in [`getPreviousDynamicPage()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dynamic-dataset/get-previous-dynamic-page.md) and [`getNextDynamicPage()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/dynamic-dataset/get-next-dynamic-page.md) methods don't guarantee that the navigation order matches the sort order of your dynamic list page. Rather, these methods determine the previous and next pages based on the [lexicographical order](https://en.wikipedia.org/wiki/Lexicographic_order) of the dynamic page relative URL's. In this tutorial, we'll use the following steps to add previous and next buttons that follow the sort order of your dynamic list page: 1. [Store item page links in local storage](#step-1--store-item-page-links-in-local-storage). 2. [Add navigation buttons to your dynamic page](#step-2--add-navigation-buttons-to-your-dynamic-page). > The code in this article was written using the following module versions: > > - @wix/site-storage (v1.21.0) > - @wix/site-location (v1.26.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Before you begin Before you start this tutorial, make sure you set up the following: 1. A dynamic list page that displays multiple items and a dynamic item page for each item. An easy way to set up these dynamic pages is with a [preset](https://support.wix.com/en/article/add-dynamic-pages-with-presets). For this tutorial, we'll use the **Team** preset as our example. Alternatively, you can set up your [dynamic pages manually](https://support.wix.com/en/article/cms-about-dynamic-pages). 2. Sort your dynamic list page in the order you want the visitors to navigate through the dynamic item pages by [sorting the dataset attached to your dynamic list page](https://support.wix.com/en/article/cms-changing-your-dynamic-page-dataset-settings). ## Step 1 | Store item page links in local storage In this step, you'll collect the unique page links for each item in the Team collection on your dynamic list page and store them in the browser's local storage. 1. Get the field ID from your collection. When you add a dynamic item page to your collection, Wix automatically creates a field containing the relative URLs for each item's page. In the **Team** collection, this field is called **Team (Item)**. To find the field ID for the **Team (Item)** field, click on the vertical ellipses that appears when you hover over the **Team (Item)** field in your collection and select **Edit**. You'll see the field ID, for example `link-team-title`, in the **Edit Field** window that pops up. 2. Import `@wix/site-storage`. ```javascript //Dynamic list page import { local } from "@wix/site-storage"; //code in next step... ``` 3. Retrieve the dynamic page URLs for every item in your collection. Use the field ID to extract the dynamic page URLs from dynamic dataset. The field ID in our example is `link-team-title`. ```javascript //...code from previous step $w.onReady(function () { $w("#dynamicDataset").onReady(() => { const repeaterData = $w("#myRepeater").data; const dynamicPageURLs = repeaterData.map(item => item["link-team-title"]); }); }); //code in next step... ``` 4. Save the dynamic page URLs in local storage. Save the list of dynamic page URLs to the browser's local storage. ```javascript //...code from previous step local.setItem('dynamicPageURLs', JSON.stringify(dynamicPageURLs)); ``` >**Note:** >If your site has more than 1 dynamic list page that links to the same dynamic item page, you need to add this code on each of those dynamic list pages. ## Step 2 | Add navigation buttons to your dynamic page In this step, you'll add the code to your dynamic item page that makes your `previousButton` and `nextButton` work. This code uses the list of dynamic page URLs you saved earlier to navigate between items in the correct order when a site visitor clicks the buttons. 1. Add navigation buttons to your dynamic item page On the dynamic item page, add 2 buttons and give them the IDs `previousButton` and `nextButton`. Don't add any links to the buttons. 1. Import `@wix/site-storage` and `@wix/site-location`. ```javascript //Dynamic item page import { local } from "@wix/site-storage"; import { location } from "@wix/site-location"; //code in next step... ``` 2. Disable the `previousButton` and `nextButton` when the page loads. ```javascript //...code from previous step $w.onReady(async function () { $w("#previousButton").disable(); $w("#nextButton").disable(); //code in next step... }); ``` 3. Retrieve the URLs from local storage. ```javascript //...code from previous step const stored = await local.getItem('dynamicPageURLs'); const dynamicPageURLs = stored ? JSON.parse(stored) : []; //code in next step... ``` 4. Get the URL of the current page. ```javascript //...code from previous step const prefix = await location.prefix(); const pathArray = await location.path(); const currentPage = '/' + prefix + '/' + pathArray.join('/'); //code in next step... ``` 5. Get the index of the current page's URL in the array of stored URLs. ```javascript //...code from previous step const currentPageIndex = dynamicPageURLs.indexOf(currentPage); //code in next step... ``` 6. If there is a URL to a previous page, set the `previousButton`'s link to that URL and enable the `previousButton`. ```javascript //...code from previous step if (currentPageIndex > 0) { $w("#previousButton").link = dynamicPageURLs[currentPageIndex - 1]; $w("#previousButton").enable(); } //code in next step... ``` 7. If there is a URL to a next page, set the `nextButton`'s link to that URL and enable the `nextButton`. ```javascript //...code from previous step if (currentPageIndex < dynamicPageURLs.length - 1 && currentPageIndex !== -1) { $w("#nextButton").link = dynamicPageURLs[currentPageIndex + 1]; $w("#nextButton").enable(); } ``` ## Full code example ```javascript //Dynamic list page import { local } from "@wix/site-storage"; $w.onReady(function () { $w("#dynamicDataset").onReady(() => { const repeaterData = $w("#myRepeater").data; const dynamicPageURLs = repeaterData.map(item => item["link-team-title"]); local.setItem('dynamicPageURLs', JSON.stringify(dynamicPageURLs)); }); }); ``` ```javascript //Dynamic item page import { local } from "@wix/site-storage"; import { location } from "@wix/site-location"; $w.onReady(async function () { $w("#previous").disable(); $w("#next").disable(); const stored = await local.getItem('dynamicPageURLs'); const dynamicPageURLs = stored ? JSON.parse(stored) : []; const prefix = await location.prefix(); const pathArray = await location.path(); const currentPage = '/' + prefix + '/' + pathArray.join('/'); const currentPageIndex = dynamicPageURLs.indexOf(currentPage); if (currentPageIndex > 0) { $w("#previous").link = dynamicPageURLs[currentPageIndex - 1]; $w("#previous").enable(); } if (currentPageIndex < dynamicPageURLs.length - 1 && currentPageIndex !== -1) { $w("#next").link = dynamicPageURLs[currentPageIndex + 1]; $w("#next").enable(); } }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create Dynamic Pages with Custom Router ## Article: Tutorial | Create Dynamic Pages with Custom Router ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/data/tutorial-create-dynamic-pages-with-custom-router.md ## Article Content: # Tutorial | Create Dynamic Pages with a Custom Router This tutorial demonstrates how to set up your own router to create and manage dynamic pages. You'll create a basic site for listing real estate properties, and then add a router and update the code to create an index page of listings and a dynamic item page. Creating a custom router gives you more control over how incoming requests to a site are handled. This tutorial uses the following steps to set up a router to create dynamic pages: - [Create a new site with a collection and router](#step-1--create-a-new-site-with-a-collection-and-router) - [Add dynamic page code](#step-2--add-dynamic-page-code) - [Add the router code](#step-3--add-the-router-code) - [Add the sitemap code](#step-4--add-the-sitemap-code) - [Test the dynamic pages](#step-5--test-dynamic-pages) > **Notes:** > - Routers currently require Velo APIs and file naming conventions. While you can use the JavaScript SDK alongside Velo, router handlers must be defined using Velo syntax in the `routers.js` backend file. > > - The code in this article was written using the following JavaScript SDK module versions: > - @wix/site-window (v1.43.0) > - @wix/site-location (v1.27.0) > - @wix/data (1.0.283) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Before you begin It's important to note the following points before doing this tutorial: - You should be familiar with the concepts of [routers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) and [dynamic pages](https://support.wix.com/en/article/cms-about-dynamic-pages). - This tutorial is done directly in the editor. However, all of the code and basic website design are relevant if you're working in the Wix IDE or your local IDE. The only differences might be the names and locations of some files. If you're having trouble locating some of these files, see the [Create a Router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/create-a-router.md) article. ## Step 1 | Create a new site with a collection and router In this step, you'll create a new site and add the features needed to build dynamic pages, including: * The collection, which you'll fill with data to populate the dynamic index and item pages. * The router, which controls the dynamic page that visitors are sent to when they request to see listings on your site. 1. Create a [new blank site](https://wixstudio.new/). 1. On the homepage, add the following 2 elements: - **Text element:** Edit the text element so its text reads "Welcome to our real estate site". - **Button:** Click on the button and select **Change Text**. Edit the text to say "Click here to see our listings". ![Click here to see our listings button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/05d12620e8ad7a36c9200284ba176a34.png) Leave the page code empty for now. 1. [Add a collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) to a site and name it `Real Estate Listings`. Copy the collection ID for later. 1. Add the following 4 fields to the collection: - **Title**: Text field that contains the name of the listing. - **Description**: Text field that contains a brief description of the listing. - **Image**: Image field that contains an image of the property in the listing. - **Slug**: Text field that contains the slugified name of the listing. Once you set up your fields, add the following items to the collection exactly as shown in the table: | Title | Description | Image | Slug | | ------| ------------ | ------ | ------- | | Modern urban apartment | This newly built apartment has all the trimmings and appliances you need in the modern-day city. | wix:image://v1/11062b_1c8c953e6532482b974d6b30961091e2~mv2.jpg/Loft%20Style%20Apartment.jpg#originWidth=4000&originHeight=2667 | modern-urban-apartment | | Beach house | Imagine stepping outside your house and you're right on the beach. You can now achieve that dream with this piece of property. | wix:image://v1/d4dde1_954ed2d26fb14166ad634c0299d0ed2c~mv2.jpg/Large%20house%20right%20next%20to%20the%20sea.%20It%20is%20light%20blue%20with%20white%20trim%20and%20doors.%20It%20has%20a%20po#originWidth=500&originHeight=500 | beach-house | | Cute Cottage | This adorable cottage in the suburbs reminds you of old times outside while keeping a modern feel inside. | wix:image://v1/d4dde1_1f312bbf4d8743a695305a3894ac89fb~mv2.jpg/Cottage%20made%20of%20white%20stone%20with%20a%20small%20green%20lawn%20and%20a%20white%20picket%20fence%20with%20a%20gate.%20#originWidth=500&originHeight=500 | cute-cottage | | Cozy Cabin | Nestled deep in the woods, this cabin offers the peace and quiet of the wilderness away from the city. | wix:image://v1/d4dde1_0fe31ec5d1bb4ed184ff61fd6b42ce20~mv2.jpg/A%20wooden%20cabin%20nestled%20in%20between%20trees..jpg#originWidth=500&originHeight=500 | cozy-cabin | 1. [Add a router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/create-a-router.md) to your site and set the URL prefix to `listings`. 1. Navigate to the **Backend & Public** section of the Code panel and locate the `routers.js` file. It should contain 2 methods, `listings_Router` and `listings_SiteMap`, with the same sample code explained in [Create a Router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/create-a-router.md). Additionally, a router page called `listings-page` should now appear in the Code panel. ## Step 2 | Add dynamic page code In this step, you'll add the page code that displays property listings for 2 router pages: the index page and the dynamic item pages. Start by coding the index page, which will display all of the listings for visitor selection: 1. In the **Page Code** section of the Code panel, under **Router Pages** find and open the `listings_page` in the editor. 1. Add a [repeater](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids) to the page. Select an item in the repeater and open the settings menu. Click **Quick Add** to add an image and a title to the repeater item. ![Repeater settings menu with Quick Add option highlighted, showing image and title elements to add](https://wixmp-833713b177cebf373f611808.wixmp.com/images/64bd1ecd4d2e08edf003bf6defa1d387.png) 1. Select the repeater and rename the ID of the repeater to `listings` using the [Properties & Events Panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md). ![Properties & Events Panel showing ID field with 'listings' entered as the new repeater ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/28380b902072e88aad787e26b0406f80.png) Repeat this step with the IDs of the repeater item, title, and image that you previously added to the repeater. Select each element and give them the following IDs: - Repeater item: `listing` - Title: `listingTitle` - Image: `listingImage` 1. In the code editor for `listings-page`, add the following code: This code retrieves data from the router and populates the repeater with listing information, setting up click handlers to navigate to individual item pages. The router controls which dynamic item page to render based on the specified slug. ```js import { getRouterData } from '@wix/site-window'; import { to } from '@wix/site-location'; $w.onReady(function () { const listings = getRouterData(); $w('#listings').onItemReady( ($item, itemData) => { $item('#listingImage').src = itemData.image; $item('#listingTitle').text = itemData.title; $item('#listing').onClick( () => { to(`/listings/${itemData.slug}`); }); } ); $w('#listings').data = listings; }); ``` The index page is now set up so that it will display all of the items in the collection. Next, add dynamic item router page: 1. In the **Page Code** section of the Code panel, under **Router Pages**, click the **More Actions** button next to **Listings Pages (Router)**. Select **Add page to router**. ![Code panel showing More Actions menu next to Listings Pages (Router) with 'Add page to router' option highlighted](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0880f50c08320f50312b121c2003a312.png) 1. Label the new page `listing` and click **Add Page**. You'll see a second router page under **Listings Pages (Router)**. 1. Select the `listing` page so it appears in the editor. Typically, the page would include more detail, such as price and contact information. However, for this example, keep the dynamic item page simple with a single image, title, and description. Add an image and 2 text elements to the page, and arrange the elements so the page layout looks like this: ![Dynamic item page layout showing an image at the top, followed by a title and description text below](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c0c6e41ab94b1bdbbe6de9c39ccf6d9c.png) 1. In the code editor, open the page code for `listing`. Add the following code: This code retrieves the specific listing data from the router and populates the elements with it on the dynamic item page. ```js import { getRouterData } from '@wix/site-window'; $w.onReady(function () { const listing = getRouterData(); $w('#listingTitle').text = listing.title; $w('#listingDescription').text = listing.description; $w('#listingImage').src = listing.image; }); ``` You now have a functioning index and dynamic item page that will render your collection data based on requests to the router. ## Step 3 | Add the router code This step implements the backend logic that handles visitor requests and determines which page to display. 1. Open the `routers.js` file located in the **Public & Backend** section of the Code panel. 1. Import the [Wix Data API](https://dev.wix.com/docs/sdk/backend-modules/data/introduction.md). The sample code already includes the import statement for `wix-router`. Your code should look like this: ```js import {ok, notFound, WixRouterSitemapEntry} from "wix-router"; import { items } from "@wix/data"; ``` The sample code also includes an object with hard-coded data. You can delete this object as it's not necessary for this tutorial. 1. In the `listings_Router()` method, delete the sample code and replace it with the code below. This code checks if the visitor is requesting a specific item page or the index page. If the `path` variable is empty, the code queries the collection and displays the index page with all property listings. It also provides SEO data for search engines. ```js export async function listings_Router(request) { if (!request.path[0]) { const { documents: listings } = await items.query('RealEstateListings').find(); const seoData = { title: 'Property Listings', description: 'List of available properties', noIndex: false }; return ok('listings-page', listings, seoData); } } ```
__Tip:__ To find your collection ID, go to **CMS** > **Your Collections** in the sidebar. Open the menu for your collection and click **Edit settings**. Copy the collection ID that appears when the modal opens.
1. Add the code below after the `if` statement you previously added. This code handles the following 2 cases when the `path` variable is _not_ empty: - The visitor requests an existing path. - The visitor requests a path that doesn't exist. The code queries the collection for a matching `slug` and returns the specific listing with SEO data. If the item doesn't exist in the collection, an error page is displayed. ```js else { const results = await items.query('RealEstateListings') .eq('slug', request.path[0]) .find(); if (results) { const listing = results.items[0]; const seoData = { title: listing.title, metaTags: [ { name: 'description', content: listing.description }, { name: 'og:title', content: listing.title }, { property: 'og:image', content: listing.image } ], }; return ok('listing', listing, seoData); } else { return notFound(); } } ``` ## Step 4 | Add the sitemap code Update the sitemap method that helps search engines discover and index your dynamic pages. In the `routers.js` file, find the `listings_SiteMap()` method and replace the sample code with the code below. This code queries the collection to get all listings and creates a sitemap entry for each item using its slug and title. It then adds the index page to ensure search engines can discover all your dynamic pages: ```js export async function listings_SiteMap(sitemapRequest) { const { items: listings } = await items.query('RealEstateListings').find(); const siteMapEntries = listings.map(listing => { const entry = new WixRouterSitemapEntry(listing.title); entry.pageName = 'listing'; entry.url = '/listings/' + listing.slug; entry.title = listing.title; return entry; }); const indexEntry = new WixRouterSitemapEntry('Property Listings'); indexEntry.pageName = 'listings-page'; indexEntry.url = '/listings'; indexEntry.title = 'Property Listings'; siteMapEntries.push(indexEntry); return siteMapEntries; } ``` Here's the complete code for the `routers.js` file, including both the router and sitemap methods: ```js import {ok, notFound, WixRouterSitemapEntry} from "wix-router"; import { items } from "@wix/data"; export async function listings_Router(request) { if (!request.path[0]) { const { items: listings } = await items.query('RealEstateListings').find(); const seoData = { title: 'Property Listings', description: 'List of available properties', noIndex: false }; return ok('listings-page', listings, seoData); } else { const results = await items.query('RealEstateListings') .eq('slug', request.path[0]) .find(); if (results) { const listing = results.items[0]; const seoData = { title: listing.title, metaTags: [{ name: 'description', content: listing.description }, { name: 'og:title', content: listing.title }, { property: 'og:image', content: listing.image }], }; return ok('listing', listing, seoData); } else { return notFound(); } } } export async function listings_SiteMap(sitemapRequest) { const { items: listings } = await items.query('RealEstateListings').find(); const siteMapEntries = listings.map(listing => { const entry = new WixRouterSitemapEntry(listing.title); entry.pageName = 'listing'; entry.url = '/listings/' + listing.slug; entry.title = listing.title; return entry; }); const indexEntry = new WixRouterSitemapEntry('Property Listings'); indexEntry.pageName = 'listings-page'; indexEntry.url = '/listings'; indexEntry.title = 'Property Listings'; siteMapEntries.push(indexEntry); return siteMapEntries; } ``` ## Step 5 | Test dynamic pages In this step, you'll check that the router and dynamic pages work correctly by testing them on a published site. 1. Click **Publish** in the top right corner of the editor. 1. Open the menu in the top left corner and click **Site** > **View Published Site**. ![Editor menu opened showing Site option with 'View Published Site' highlighted](https://wixmp-833713b177cebf373f611808.wixmp.com/images/288053bdb3f77fb88c6199a9ed0e7214.png) The published site opens in a new tab. ![Published homepage displaying 'Welcome to our real estate site' text and 'Click here to see our listings' button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1d1cd520ed4477d14aac097973bb6845.png) 1. Click the button on the homepage to go to the listings index page. Each time you click on a listing from the index page, you make a request to the router. The router redirects you to the item page, fetches the item data from the `Real Estate Listings` collection, and renders it on the page. ![Individual property item page showing a single property with its image, title, and description](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bfa074e998490f6b3c9470c340756739.png) Now that you know your router is working, take a look at the sitemap: 1. Return to your homepage and in the address bar, add `/sitemap.xml` to your site address: ```bash https://mysite.wixstudio.io/test-site/sitemap.xml ``` 1. Navigate to the new address and you'll see your site's sitemap displayed in XML. For example: ```xml https://mysite.wixstudio.io/test-site/dynamic-listings-sitemap.xml 2024-10-14 https://mysite.wixstudio.io/test-site/pages-sitemap.xml 2024-10-14 ``` 1. Copy the link that goes to your router page and paste it back into the address bar. Navigate to the link to see the sitemap for your router: ```xml https://mysite.wixstudio.io/test-site/listings/cozy-cabin 2024-10-14 https://mysite.wixstudio.io/test-site/listings/beach-house 2024-10-14 https://mysite.wixstudio.io/test-site/listings/modern-urban-apartment 2024-10-14 https://mysite.wixstudio.io/test-site/listings 2024-10-14 https://mysite.wixstudio.io/test-site/listings/cute-cottage 2024-10-14 ``` This is the sitemap generated by your `listings_SiteMap()` method. Congratulations! You've created a custom router and successfully set up dynamic pages for a real estate listings site. You can continue building on this router example to enhance the site design, or apply what you learned in this tutorial to create dynamic pages for an entirely different type of content. ## See also * [About routers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) * [Create a router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/create-a-router.md) * [About SEO and routing](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/add-seo-to-your-router.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Display Database Collection Content in a Repeater ## Article: Tutorial | Display Database Collection Content in a Repeater ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/data/tutorial-display-database-collection-content-in-a-repeater.md ## Article Content: # Tutorial | Display Database Collection Content in a Repeater This tutorial for beginners explains how to display database collection content in a repeater using code. > **Note:** You can also [display database content in a repeater](https://support.wix.com/en/article/displaying-collection-content-in-a-repeater) without any code using a dataset, but using code provides you with additional functionality and options. > The code in this article was written using the following module versions: > > - @wix/data (v1.0.244) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## About database collections and repeaters [Database collections](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md) store your site's data in structured records. Whether you're managing property listings on a real estate site or storing form submissions, collections organize your content into manageable datasets. [Repeaters](https://support.wix.com/en/article/wix-editor-adding-and-managing-repeaters-lists-grids) are great for displaying collection data because they create consistent layouts for multiple items. Think of them as templates that repeat for each record in your collection, for example, showcasing property listings, blog posts, or product catalogs. We'll use the following steps to display database collection content in a repeater: 1. Query the database collection to retrieve your data. 2. Bind the data to your repeater component. 3. Map collection fields to specific repeater elements. The result should look like this: ![Repeater displaying collection content](https://wixmp-833713b177cebf373f611808.wixmp.com/images/34976e758c70d518dbfcb4f948e0f410.png) ## Step 1 | Add a repeater on your site Add a [repeater](https://support.wix.com/en/article/studio-editor-adding-and-customizing-repeaters) on your site. ## Step 2 | Open the page code file Open the code file for your page where you added the repeater. The way you open the file depends on the IDE you're using. ::::tabs :::Editor 1. Navigate to **Page Code** in the Code sidebar. 1. Select your page. A page code file opens. The file includes a sample `onReady()` function. ::: :::Wix-IDE-or-Local-IDE Open the file in the `Pages` folder. The file includes a sample `onReady()` function. ::: :::: ## Step 3 | Query the database collection This step retrieves all records from your collection and prepares them for display. To query a collection, you'll need to get the collection ID. The way you get the ID depends on the editor you're using: ::::tabs :::Wix-Studio 1. Click on the **CMS** tab in the Code panel. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::Wix-Editor 1. Click on the **Databases** tab in the Code sidebar. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::: Now that you have the collection ID, you can query your data: 1. Import the [`@wix/data`](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md) module, which provides the [`query()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md) method for retrieving collection data. ```javascript import { items } from "@wix/data"; ``` > Learn more about using [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) when developing sites. 2. Run the query using the ID you retrieved above: ```javascript const { items: collectionData } = await items.query("MyCollection").find(); ``` The query returns an array of [`items`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md), where each item represents a record from your collection. Collection [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) become object properties, so fields such as `title`, `price`, `image`, and `url` appear as properties with the same names in each result object. ```json [ { "_id": "1234", "_createdDate": "2021-05-29T08:35:52.344Z", "title": "DOWN AVENUE", "price": 500.000, "image": "wix:image://v1/68d3a9_1de7529c444b4c9eb38401f8efe0cad2.jpg/down-avenue.jpg", "url": "https://users.wixsite.com/best-real-estate-site/listings/down-avenue" }, { "_id": "5678", "_createdDate": "2021-05-25T12:48:56.572Z", "title": "QUEENS WAY", "price": 450.000, "image": "wix:image://v1/79e4b0_2ee8539d456c5c0fb48502g9fge1dbe4.jpg/queens-way.jpg", "url": "https://users.wixsite.com/best-real-estate-site/listings/queens-way" }, ... ] ``` ## Step 4 | Bind the data to a repeater This step connects your queried collection data to the repeater, creating individual items for each record. Set the repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) property to your collection results: ```javascript $w("#myRepeater").data = collectionData; ``` > **Note:** This automatically triggers the repeater's [onItemReady()](https://www.wix.com/velo/reference/$w/repeater/onitemready) event handler, which we'll use in the next step. ## Step 5 | Map collection fields to specific repeater elements This step defines how collection fields populate specific elements within each repeater item. For example, a `title` field might display in a text element, while a `url` field connects to a button's link property. Use the [`onItemReady()`](https://www.wix.com/velo/reference/$w/repeater/onitemready) event handler to map your data. This event runs automatically for each new element in the repeater's `.data` array and is used to connect collection values to repeater elements. The handler receives the [`$item`](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) selector, which selects specific instances of repeated elements. In this example, `title`, `price`, `image`, and `url` are [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) from the `MyCollection` database collection: ```javascript $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#myTitleText").text = itemData.title; $item("#myPriceText").text = itemData.price; $item("#myImage").src = itemData.image; $item("#myButton").link = itemData.url; }); ``` After applying the code to your page, you'll see your database content displayed in your repeater. ## Complete code Here is the complete code for the example: ```javascript import { items } from "@wix/data"; $w.onReady(async method () { const { items: collectionData } = await items.query("MyCollection").find(); $w("#myRepeater").data = collectionData; $w("#myRepeater").onItemReady(($item, itemData, index) => { $item("#myTitleText").text = itemData.title; $item("#myPriceText").text = itemData.price; $item("#myImage").src = itemData.image; $item("#myButton").link = itemData.url; }); }); ``` ## See also - [$item](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) - [Wix JavaScript SDK](https://dev.wix.com/docs/sdk/articles/get-started/about-the-wix-java-script-sdk.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Call APIs with Secrets ## Article: Tutorial | Call APIs with Secrets ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/integrations/tutorial-call-apis-with-secrets.md ## Article Content: # Tutorial | Call APIs with Secrets In this tutorial, you'll learn how to securely use an external API key stored in the [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) to call the [OpenWeatherMap API](https://openweathermap.org/api). By keeping API keys safe and confidential with the Secrets Manager, you can make secure API requests without exposing sensitive information in your code. At the end of this tutorial, you'll have a working [web method](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction.md) that securely retrieves weather data for any city, using a stored API key. We'll use the following steps to build this functionality: 1. Set up your API key in the Secrets Manager. 2. Create a web method to retrieve the API key and call the OpenWeatherMap API. ## Before you begin It's important to note the following before doing this tutorial: - You'll need an OpenWeatherMap API key. You can get one by signing up for a free account at [OpenWeatherMap](https://openweathermap.org/api). > The code in this article was written using the following module versions: > > - @wix/web-methods (v1.0.0) > - @wix/secrets (v1.0.0) > - @wix/essentials (v1.0.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Step 1 | Set up your API key in the Secrets Manager This step stores your OpenWeatherMap API key securely in the Secrets Manager. At the end of this step, your API key will be safely stored and ready to use in your code. To set up your API key: 1. Access the Secrets Manager using one of these methods: - In the editor, navigate to **Developer Tools** in the Code sidebar. Under the Security section, select **Secrets Manager**. - In the dashboard, select **Developer Tools**, and then select **Secrets Manager**. 2. Add and save the API key for the OpenWeatherMap API. For purposes of this tutorial, name this key "WeatherApiKey". ## Step 2 | Create a web method to retrieve weather data This step creates a backend web method that retrieves your API key from the Secrets Manager and uses it to call the OpenWeatherMap API. At the end of this step, you'll have a working function that can fetch weather data for any city. To create the web method: 1. Create a new file called `serviceModule.web.js` in the site's backend folder. 2. Add the import statements at the top of the file: ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import { secrets } from "@wix/secrets"; import { auth } from "@wix/essentials"; ``` 3. Create an [elevated](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-elevated-permissions.md) function to access site secrets with proper permissions: ```javascript const elevatedGetSecretValue = auth.elevate(secrets.getSecretValue); ``` 4. Create a web method function that accepts a `city` parameter and retrieves the API key you stored in a site secret: ```javascript export const getCurrentTemp = webMethod(Permissions.Anyone, async (city) => { const { value } = await elevatedGetSecretValue("WeatherApiKey"); // Continue with the API call code in the next steps }); ``` 5. Continue the function by constructing the full URL for the fetch request: ```javascript const url = "https://api.openweathermap.org/data/2.5/weather?q="; const fullUrl = url + city + "&APPID=" + value; ``` 6. Finish the function by making the API call and returning the temperature data: ```javascript return fetch(fullUrl) .then((response) => response.json()) .then((json) => json.main.temp); ``` You've now successfully created a secure weather API integration using the Wix JavaScript SDK and the Secrets Manager. You can extend this example to retrieve additional weather data or integrate it into your site's user interface.
**Tip:** You can test backend code in the editor without developing a frontend using Wix's [functional testing](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/about-functional-testing.md) feature.
## Complete code example Here's the complete code for your `serviceModule.web.js` file: ```javascript // serviceModule.web.js import { Permissions, webMethod } from "@wix/web-methods"; import { secrets } from "@wix/secrets"; import { auth } from "@wix/essentials"; const elevatedGetSecretValue = auth.elevate(secrets.getSecretValue); export const getCurrentTemp = webMethod(Permissions.Anyone, async (city) => { const { value } = await elevatedGetSecretValue("WeatherApiKey"); const url = "https://api.openweathermap.org/data/2.5/weather?q="; const fullUrl = url + city + "&APPID=" + value; return fetch(fullUrl) .then((response) => response.json()) .then((json) => json.main.temp); }); ``` ## See also - [Web Methods API](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction.md) - [Elevated Permissions](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-elevated-permissions.md) - [Secrets API](https://dev.wix.com/docs/api-reference/business-management/secrets/introduction?apiView=SDK.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Call External APIs with Fetch ## Article: Tutorial | Call External APIs with Fetch ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/integrations/tutorial-call-external-apis-with-fetch.md ## Article Content: # Tutorial | Call External APIs with Fetch In this tutorial, you'll learn how to use the fetch API to connect to an external service and display data items retrieved from that service. Site visitors can click a button to fetch and display a collection of greetings in different languages. > **Note:** To see this example in action, see [Hello Fetch](https://dev.wix.com/docs/coding-examples/getting-started/hello-world/hello-fetch.md). By the end of this tutorial, you'll have a working example that fetches greetings data from an external API and displays it on your site using a repeater element. ![Fetch many example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4789c3978a0be343919ee225b9083e6d.png) ## Step 1 | Add elements to your page Add the following elements to your page: - A [button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) to fetch the greetings. Set the button ID to `fetchGreetingsButton`. - A [repeater](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/introduction.md) to display multiple data items. Set the repeater ID to `greetingsRepeater`. - A [text](https://dev.wix.com/docs/velo/velo-only-apis/$w/text/introduction.md) element to display the greeting's language inside the repeater. Set the text element ID to `repeatedLanguage`. - A text element to display the greeting inside the repeater. Set the text element ID to `repeatedGreeting`. ## Step 2 | Add backend code Create a new [web module](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) file called `dataFetcher.web.js` and add the following code to fetch greetings from an external API: ```javascript import { webMethod, Permissions } from "wix-web-module"; export const getGreetings = webMethod(Permissions.Anyone, async () => { try { const response = await fetch( "https://velo-examples.wixstudio.com/examples-backends/_functions/greetings" ); const json = await response.json(); return json; } catch (error) { return { error: "Failed to fetch greetings." }; } }); ``` ## Step 3 | Add frontend code In the code for the page where you added the elements above: 1. Add the import statement, hide the repeater, and set up a button click event handler: ```javascript import { getGreetings } from "backend/dataFetcher.web.js"; $w.onReady(function () { $w("#greetingsRepeater").hide(); $w("#fetchGreetingsButton").onClick(async () => { // Button click logic will go here }); }); ``` 1. Disable the button during fetch to prevent multiple clicks: ```javascript $w("#fetchGreetingsButton").disable(); ``` 1. Call the backend function to retrieve the greetings data: ```javascript const fetchedData = await getGreetings(); ``` 1. Transform the data by adding `_id` properties, as required by repeater elements: ```javascript const repeaterData = fetchedData.map((item) => { item._id = item.id.toString(); return item; }); ``` 1. Populate the repeater with the transformed data: ```javascript $w("#greetingsRepeater").data = repeaterData; ``` 1. Make the previously hidden repeater visible: ```javascript $w("#greetingsRepeater").show(); ``` 1. Change the button label to indicate completion: ```javascript $w("#fetchGreetingsButton").label = "Done"; ``` This completes the code requirements within the `onReady` function. 1. In a new code block, set up how each item in the repeater displays its data: ```javascript $w("#greetingsRepeater").onItemReady(($item, itemData) => { $item("#repeatedLanguage").text = itemData.language; $item("#repeatedGreeting").text = itemData.greeting; }); ``` ## Complete code example ### Backend ```javascript // In the dataFetcher.web.js file import { webMethod, Permissions } from "wix-web-module"; export const getGreetings = webMethod(Permissions.Anyone, async () => { try { const response = await fetch( "https://velo-examples.wixstudio.com/examples-backends/_functions/greetings" ); const json = await response.json(); return json; } catch (error) { return { error: "Failed to fetch greetings." }; } }); ``` ### Frontend ```javascript import { getGreetings } from "backend/dataFetcher.web.js"; $w.onReady(function () { $w("#greetingsRepeater").hide(); $w("#fetchGreetingsButton").onClick(async () => { $w("#fetchGreetingsButton").disable(); const fetchedData = await getGreetings(); const repeaterData = fetchedData.map((item) => { item._id = item.id.toString(); return item; }); $w("#greetingsRepeater").data = repeaterData; $w("#greetingsRepeater").show(); $w("#fetchGreetingsButton").label = "Done"; }); $w("#greetingsRepeater").onItemReady(($item, itemData) => { $item("#repeatedLanguage").text = itemData.language; $item("#repeatedGreeting").text = itemData.greeting; }); }); ``` ## See also - [Web Methods API](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction.md) - [The Lifecycle of Repeated Items](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/the-lifecycle-of-repeated-items.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Custom Registration Form ## Article: Tutorial | Create a Custom Registration Form ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-create-a-custom-registration-form.md ## Article Content: # Tutorial | Create a Custom Registration Form When a site adds a [Members Area](https://support.wix.com/en/article/site-members-about-the-members-area), it includes a default registration form that allows site visitors to sign up as members. Sometimes the standard form doesn't meet the specific needs of a site. In this tutorial, we build a custom registration form that can be designed to match a site's branding, and use it to collect specific information from site visitors in the registration process. Here we create an example custom registration form that includes confirmation fields for email address and password. In this tutorial, you'll learn how to: - [Enable a custom site registration form.](#step-1--enable-custom-site-registration) - [Build a custom form with custom input and design elements.](#step-2--build-a-custom-form) - [Write page code to validate confirmation fields.](#step-3--add-frontend-code) - [Write page code to register a new member](#step-4--add-registration-logic) ## Before you begin It's important to note the following points before doing this tutorial: - Your site must have [a Members Area](https://support.wix.com/en/article/site-members-add-and-set-up-a-members-area). > The code in this tutorial was written using the following module versions: > > - @wix/sdk (v1.12.0) > - @wix/members (v1.0.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Step 1 | Enable custom site registration To get started, follow the steps below to enable custom site registration using custom forms. 1. [Add a popup to a site](https://support.wix.com/en/article/studio-editor-using-popups#adding-a-popup). In our example, we [name our popup](https://support.wix.com/en/article/studio-editor-using-popups#customizing-a-popup) **Custom Registration Form**. 2. Navigate to **Pages** on the left side of your editor. Under **Signup & Login**, click **Signup (Default)**. ![Signup & Login pages menu](https://wixmp-833713b177cebf373f611808.wixmp.com/images/199c3d82b20e6efbe1f959706fd60839.png) 3. In the dropdown menu, change the default form to **Velo Form**, and link it to the **Custom Registration Form** popup created earlier. This step changes the default member signup form to the custom registration form. ![Link custom registration form](https://wixmp-833713b177cebf373f611808.wixmp.com/images/932470693e4303eabee3d13d516e066d.png) > **Note:** When a site visitor signs up using a Velo custom registration form, they automatically become a site member, as the member signup settings default to automatic approval for `Everyone who signs up`. To require manual approval before becoming a member, adjust the settings in **Additional Signup & Login Settings**. You can now go to the popup page to start building the form. ## Step 2 | Build a custom form To add fields to a custom registration form: 1. Click on **Signup (Custom)**, under **Signup & Login**. This opens the popup. 2. To add fields to the popup, click Add Elements ![Add Elements icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b720ae0219e0c5e373ab3fd18a634caf.png "Add Elements icon") on the left side of the editor. Then, drag and drop the relevant element. 3. Add the following fields: - A text input for entering a first name. Set the text ID to `firstName`. - A text input for entering a last name. Set the text ID to `lastName`. - An email input for entering an email address. Set the email ID to `email`. - An email input for confirming an email address. Set the email ID to `confirmEmail`. - A password input for entering a password. Set the password ID to `password`. - A password input for confirming a password. Set the password ID to `confirmPassword`. 4. Add text elements below the confirmation fields to display error messages if a site visitor's entries don't match. Set the default values for these text elements as "Hidden" in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md). - A text element for noting the 2 email fields don't match. Set the text ID to `emailMismatchText`. - A text element for noting the 2 password fields don't match. Set the text ID to `passwordMismatchText`. 5. Add a register button to submit the form and register the site visitor as a new member. Set the button ID to `register`. The form should look like this: ![Custom registration form layout](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4063bbb25ca381b916de21576067953e.png) > **Note:** A registration form must include input elements to collect a site visitor's email and password. This data is required for adding a new contact entry for a site visitor in a site's Contact List, and for registering them as a site member. > > If you want to collect additional data from site visitors aside from the default fields in a site's Contact List, you'll need to add custom fields to the site's Contact List. ## Step 3 | Add frontend code In this step, you'll add code to validate the confirmation fields in your popup. 1. Import the required module at the top of your page code. ```javascript import { authentication } from "@wix/site-members"; ``` 2. In the `onReady()` method, add an `onClick()` event handler to the register button, and retrieve the values from all input fields, including the confirmation fields. ```javascript $w.onReady(function () { $w('#register').onClick(async () => { const password = $w('#password').value; const confirmPassword = $w('#confirmPassword').value; const email = $w('#email').value; const confirmEmail = $w('#confirmEmail').value; ``` 3. Reset and hide any error messages when they're no longer needed. ```javascript // Reset error messages. $w("#emailMismatchText").hide(); $w("#passwordMismatchText").hide(); ``` 4. When a site visitor finishes entering their information and clicks **Sign Up**, first validate that the email and password confirmation fields match their respective original fields. If either validation fails, display the appropriate error message and stop the registration process. ```javascript // Validate email confirmation. if (email !== confirmEmail) { // Stop execution if emails don't match and display the error message for it. $w("#emailMismatchText").show(); return; } // Validate password confirmation. if (password !== confirmPassword) { // Stop execution if passwords don't match and display the error message for it. $w("#passwordMismatchText").show(); return; } ``` ## Step 4 | Add registration logic If the validation above passes, continue registering the new member. 1. Set the new member's contact info. > **Note:** The API requires that this is passed as an array. So even in our example, where new members only have 1 email address, this is passed as an array. ```javascript try { // Register the member using the members API. const registrationOptions = { contactInfo: { firstName: $w('#firstName').value, lastName: $w('#lastName').value, emails: [email] } }; ``` 2. Register the site visitor. ```javascript const registrationResult = await authentication.register( email, password, registrationOptions ); ``` 3. Check the registration status. ```javascript // Check the registration status. if (registrationResult.state === 'ACTIVE') { // Handle successful registration, like display a success message or redirect new member. console.log('Member registered successfully:', registrationResult); } else { // Handle pending registration cases, like email verification or manual admin approval. console.log('Registration requires additional steps:', registrationResult.state); } } catch (error) { // Handle registration errors. console.error('Registration failed:', error); } }); }); ``` ## Complete code example Here is the complete code for this example: ```javascript import { authentication } from "@wix/site-members"; $w.onReady(function () { $w("#register").onClick(async () => { const password = $w("#password").value; const confirmPassword = $w("#confirmPassword").value; const email = $w("#email").value; const confirmEmail = $w("#confirmEmail").value; // Reset error messages. $w("#emailMismatchText").hide(); $w("#passwordMismatchText").hide(); // Validate email confirmation. if (email !== confirmEmail) { // Stop execution if emails don't match and display the error message for it. $w("#emailMismatchText").show(); return; } // Validate password confirmation. if (password !== confirmPassword) { // Stop execution if passwords don't match and display the error message for it. $w("#passwordMismatchText").show(); return; } try { // Register the member using the members API. const registrationOptions = { contactInfo: { firstName: $w("#firstName").value, lastName: $w("#lastName").value, emails: [email], }, }; const registrationResult = await authentication.register( email, password, registrationOptions ); // Check the registration status. if (registrationResult.state === "ACTIVE") { // Handle successful registration, like display a success message or redirect new member. console.log("Member registered successfully:", registrationResult); } else { // Handle pending registration cases, like email verification or manual admin approval. console.log( "Registration requires additional steps:", registrationResult.state ); } } catch (error) { // Handle registration errors. console.error("Registration failed:", error); } }); }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Send a Triggered Email to Members ## Article: Tutorial | Send a Triggered Email to Members ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-a-triggered-email-to-members.md ## Article Content: # Tutorial | Send a Triggered Email to Members In this tutorial, you'll learn how to automatically send a personalized email to the currently logged-in site member using [Triggered Emails](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/about-triggered-emails.md) and an input form. You'll use a form submission as the trigger, but you can adapt this approach to send emails from anywhere in your code. You'll build a simple form where site members can enter their name, select their favorite sport, and add comments. When the form is submitted, your site will send a personalized Triggered Email to the logged-in member with the information they provided. We'll use the following steps to send a triggered email to a member: 1. [Set up your Triggered Email template.](#step-1--set-up-your-triggered-email-template) 2. [Create a form for member input.](#step-2--create-a-form-for-member-input) 3. [Send the email on form submission.](#step-3--send-the-email-on-form-submission) ## Before you begin It's important to note the following before starting this tutorial: - Members must be able to [login to your site](https://support.wix.com/en/article/adding-a-members-area-to-your-site). - Triggered Emails may not work properly in preview mode. Publish your site to test the code. - Make sure to have a collection, such as `sportsEmail`, that you can connect to a dataset later. > The code in this article was written using the following module versions: > > - @wix/site-crm (v1.31.0) > - @wix/members (v1.0.306) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Step 1 | Set up your Triggered Email template In this step, you'll create a Triggered Email template with variables for personalization. To set up your Triggered Email: 1. Go to `Developer Tools` on your site's dashboard and select `Triggered Emails`. 2. Add sender details, including the sender's name and email address. 3. Create a new Triggered Email and add variables such as `name`, `sport`, and `comments`. 4. Give your email template a meaningful name, such as `sportMail`. 5. Publish the Triggered Email. 6. Copy the code snippet for emailing site members. You'll use this code in a later step. The code should look like this: > The following code snippet uses the Velo API by importing the `'wix-crm'` package. > To use the SDK version, change the import to `'@wix/site-crm'`. ```javascript import { triggeredEmails } from 'wix-crm'; //... triggeredEmails.emailMember('sportMail', , { variables: { name: , sport: , comments: , } }); ``` > For a full, detailed walkthrough, see [Set Up a Triggered Email](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/triggered-emails/set-up-a-triggered-email.md). ## Step 2 | Create a form for member input In this step, you'll build a simple form that collects input from the site member. The form includes input fields for each variable you set up in your Triggered Email, as well as a submit button. All elements should be connected to the same dataset. To create the form: 1. Add input elements for each variable that you set up in your Triggered Email, such as name, sport, comments. In our example, we add: - An input element for entering a name. Set the input ID to `nameInput`. - A dropdown for selecting a sport. Set the dropdown ID to `sportDropdown`. - A text box for entering comments. Set the text box ID to `commentsInput`. - A button for submitting the data. Set the button ID to `submitButton`. - A dataset for connecting elements. Set the dataset ID to `sportDataset`. 2. Add a submit button. 3. Create a dataset called `sportDataset` that's connected to the `sportsEmail` collection you created earlier. 4. Connect each input element to the same dataset, such as `sportDataset`. 5. Connect the button to the same dataset and set it to the **Submit** action. ## Step 3 | Add frontend code In this step, you'll add code that sends the Triggered Email to the site member when the form is submitted. 1. Import the required modules at the top of your page code: ```javascript import { triggeredEmails } from "@wix/site-crm"; import { members } from "@wix/members"; ``` 2. Register an `onAnAfterSave()` event handler for your dataset to run the code each time the form is successfully submitted. ```javascript $w("#sportDataset").onAfterSave(async () => { // Code in next step goes here... }); ``` 3. Get the values from the input elements. ```javascript // ...Code from previous step const name = $w("#nameInput").value; const sport = $w("#sportDropdown").value; const comments = $w("#commentsInput").value; // Code in next step goes here... ``` 4. Get the currently logged in member `userId`. ```javascript // ...Code from previous step const currentMember = await members.getCurrentMember(); if (currentMember) { const userId = currentMember.member._id; // Code in next step goes here... } ``` 5. Paste the Triggered Email code snippet from earlier. Replace the placeholders in the code snippet with the values you saved from the form, and the member ID, to personalize the email. ```javascript // ...Code from previous step triggeredEmails.emailMember("sportMail", userId, { variables: { name: name, sport: sport, comments: comments, }, }); ``` ## Full example The following example includes error handling for both successful and failed email attempts. ```javascript $w.onReady(function () { $w("#sportDataset").onAfterSave(async () => { try { const name = $w("#nameInput").value; const sport = $w("#sportDropdown").value; const comments = $w("#commentsInput").value; const currentMember = await members.getCurrentMember(); if (currentMember) { const userId = currentMember.member._id; await triggeredEmails.emailMember("sportMail", userId, { variables: { name: name, sport: sport, comments: comments, }, }); console.log("email sent successfully"); } } catch (err) { console.log("error sending email", err); } }); }); ``` ## See also - [Triggered Emails](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/about-triggered-emails.md) - [Create a Triggered Email](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/create-a-triggered-email.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Send Emails with the SendGrid npm Package ## Article: Tutorial | Send Emails with the SendGrid npm Package ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-emails-with-the-send-grid-npm-package.md ## Article Content: # Tutorial | Send Emails with the SendGrid npm Package In this tutorial, you'll learn how to set up a complete email system that allows site visitors to send emails through a custom form on your Wix site. The solution uses SendGrid's API to handle email delivery while maintaining security best practices by storing sensitive API keys in Wix's Secrets Manager. SendGrid is a cloud-based email delivery platform that provides reliable email infrastructure for businesses and developers. By integrating SendGrid with your Wix site, you can send emails such as contact form submissions, order confirmations, password resets, or custom notifications to your users. This approach gives you more control over email delivery, better tracking capabilities, and higher deliverability rates compared to basic email services. To set up your site to send emails with the SendGrid service, you'll need to perform the following steps: 1. [Create a SendGrid account](#step-1--create-a-sendgrid-account). 1. [Set up your site](#step-2--set-up-the-site). 1. [Write the backend code](#step-3--write-backend-code). 1. [Write the frontend code](#step-4--write-frontend-code). > The code in this article was written using the following module versions: > > - @wix/secrets (v1.0.53) > - @wix/web-methods (v.1.0.11) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Step 1 | Create a SendGrid account To use the SendGrid service on your Wix site, you'll need to create a [SendGrid account](https://signup.sendgrid.com/). The email address you verify in the account will be the sender address for the emails you send from your Wix site. Once you've created an account, [create an API key](https://www.twilio.com/docs/sendgrid/ui/account-and-settings/api-keys#creating-an-api-key). Copy the key from your dashboard and store it in the Secrets Manager in your Wix site (see the next step). ## Step 2 | Set up the site In this step, you'll configure your Wix site by securely storing your SendGrid credentials and installing the required npm package. For security purposes, it's best to store sensitive content such as API keys in the [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md): 1. Navigate to **Developer Tools** in the Code sidebar. 1. Under the **Security** section, select **Secrets Manager**. 1. In the top right, click **Add Secret**. 1. Set the **Secret Name** to `sendGridSecret`. 1. Store both your SendGrid API key and the verified email address associated with your SendGrid account in the secret value. Your secret value will look like this: ```json { "key": "AB.CdPEfg1HIJk-LMnoPq2R3s.T4uVwxyZAbcd567eF8fghIjKL9mN", "senderEmail": "janedoe@example.com" } ``` 1. Click **Save**. 1. Go back to the site editor and install the [npm package](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md). ## Step 3 | Create a custom form On your **Home** page, create a custom form and add the following elements: - Input elements for the recipient's email and subject line. Give elements the IDs of `#toEmail` and `#subject`. - Text box for the email body text. Give the element the ID of `#emailContent`. - Submit button for sending the email. Give the element the ID of `#sendButton`. - Text element for displaying success and error messages. Give the element the ID of `#messageText`. ## Step 4 | Add backend code In this step, you'll securely retrieve your SendGrid credentials and handle the email sending process using SendGrid's API. 1. Add a [web module](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) to your site code and call it `sendEmail.web.js`. 1. Import the modules you'll need: ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import sgMail from "@sendgrid/mail"; import { secrets } from "@wix/secrets"; ``` 1. In the `sendEmail` function, get the SendGrid API key and sender email address that you saved in the Secrets Manager, and set the API key to enable usage of the SendGrid service. For this, use the [Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md): ```javascript export const sendEmail = webMethod(Permissions.Anyone, async (recipient, subject, body) => { const sendGridSecret = JSON.parse((await secrets.getSecretValue('sendGridSecret')).value); const key = sendGridSecret.key; const senderEmail = sendGridSecret.senderEmail; sgMail.setApiKey(key); // Function continues below ``` 1. Set the data for the email to be sent, such as the recipient's email address and the text of the email body. The sender email was extracted from the Secrets Manager and the rest of the information is entered by site visitors on the frontend by using input elements. Call the `send()` function to send the email. ```javascript // Function continued from above const msg = { from: senderEmail, to: recipient, subject: subject, text: body }; try { const result = await sgMail.send(msg); return { success: true, result: result }; } catch (error) { console.error('Error sending the email: ' + error.message); return { success: false, error: error.message }; } }); ``` ## Step 5 | Add frontend code In this step, you'll handle user interactions with the email form, validate input fields, and communicate with the backend function to send emails. 1. Open the page code file and import the function from the backend: ```javascript import { sendEmail } from "backend/sendEmail.web"; ``` 1. Add the `checkFormFields()` function to make sure all form field input values are valid before sending an email: ```javascript function checkFormFields() { return ( $w("#toEmail").validity.valid && $w("#subject").validity.valid && $w("#emailContent").validity.valid ); } ``` 1. Add the `clearFields()` function to reset the form fields following a successful email transmission. The `resetValidityIndication()` function resets the inputs' visual validity indications. Some elements have a visual cue, such as a red outline, that indicates when the current value isn't valid. This function clears any indications. ```javascript function clearFields() { $w("#toEmail").value = ""; $w("#subject").value = ""; $w("#emailContent").value = ""; $w("#toEmail").resetValidityIndication(); $w("#subject").resetValidityIndication(); $w("#emailContent").resetValidityIndication(); } ``` 1. Add the `displayMessage()` function to temporarily show a success or error message following an attempt to send an email: ```javascript function displayMessage(message) { $w("#messageText").text = message; $w("#messageText").show(); $w("#messageText").hide("fade", { delay: 5000 }); } ``` 1. When a site visitor clicks the **Send** button, do the following: 1. Check whether all form field values are valid. 2. If they're valid, send the email with the values extracted from the form inputs. If the form fields aren't valid, show an error message. 3. If the email transmission request is successful, clear the form fields and display a success message. If the request fails, show an error message. ```javascript $w.onReady(function () { $w("#sendButton").onClick(async () => { const passedValidations = checkFormFields(); if (passedValidations) { const emailResult = await sendEmail( $w("#toEmail").value, $w("#subject").value, $w("#emailContent").value ); if (emailResult.success) { clearFields(); displayMessage("Email was sent"); } else { displayMessage("Error sending email: " + emailResult.error); } } else { displayMessage("Validation error, please review your input fields."); } }); }); ``` ## Example code Here is the complete code for this example: ### Backend code file ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import sgMail from "@sendgrid/mail"; import { secrets } from "@wix/secrets"; export const sendEmail = webMethod( Permissions.Anyone, async (recipient, subject, body) => { const sendGridSecret = JSON.parse( (await secrets.getSecretValue("sendGridSecret")).value ); const key = sendGridSecret.key; const senderEmail = sendGridSecret.senderEmail; sgMail.setApiKey(key); const msg = { from: senderEmail, to: recipient, subject: subject, text: body, }; try { const result = await sgMail.send(msg); return { success: true, result: result }; } catch (error) { console.error("Error sending the email: " + error.message); return { success: false, error: error.message }; } } ); ``` ### Frontend code file ```javascript import { sendEmail } from "backend/sendEmail.web"; $w.onReady(function () { $w("#sendButton").onClick(async () => { const passedValidations = checkFormFields(); if (passedValidations) { const emailResult = await sendEmail( $w("#toEmail").value, $w("#subject").value, $w("#emailContent").value ); if (emailResult.success) { clearFields(); displayMessage("Email was sent."); } else { displayMessage("Error sending email: " + emailResult.error); } } else { displayMessage("Validation error, please review your input fields."); } }); }); function checkFormFields() { return ( $w("#toEmail").validity.valid && $w("#subject").validity.valid && $w("#emailContent").validity.valid ); } function clearFields() { $w("#toEmail").value = ""; $w("#subject").value = ""; $w("#emailContent").value = ""; $w("#toEmail").resetValidityIndication(); $w("#subject").resetValidityIndication(); $w("#emailContent").resetValidityIndication(); } function displayMessage(message) { $w("#messageText").text = message; $w("#messageText").show(); $w("#messageText").hide("fade", { delay: 5000 }); } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Generate Logs to Debug a Site ## Article: Tutorial | Generate Logs to Debug a Site ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/monitoring/tutorial-generate-logs-to-debug-a-site.md ## Article Content: # Tutorial | Generate Logs to Debug a Site This tutorial demonstrates how to generate and view logs from site code. The logs are viewed using [Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md). In this tutorial, you'll: 1. [Add a button to a site](#step-1--add-a-button-to-a-site) 1. [Generate and view a log](#step-2--generate-and-view-a-log) > **Note**: In this tutorial, logs are generated from page code. However, you can also generate logs from backend code. ## Step 1 | Add a button to a site Add a button to a site and create an event handler: 1. Add a button labeled `button1` to a site's home page. Here's how it should look: ![Button added to site home page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/949a3dd03f5f11dd9dc3211af1b2c85f.png) 1. In the site's home page code, add an [event handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) that runs whenever the button is clicked. > **Note**: This tutorial uses the `$w()` selector for frontend element interaction, which is a Velo API that remains necessary for frontend code even when using the JavaScript SDK for backend development. For the purposes of this tutorial, produce an error by defining `factor3` instead of `factor2.` ```js $w("#button1").onClick(() => { const factor1 = 10; const factor3 = 5; try { console.log("Button clicked!"); console.log(factor1 + " * " + factor2 + " = " + factor1 * factor2); } catch (error) { console.error("An error occurred during multiplication:", error); console.warn("Make sure to specify 2 factors for multiplication."); } }); ``` 1. Preview or publish the site. > **Note:** You can generate logs when previewing a site, but some log information is available only when the site is published. ## Step 2 | Generate and view a log Generate a log and view it [using Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-wix-logs.md) as follows: 1. In a separate browser tab or window, [open the Wix Logs page](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-wix-logs.md#view-the-logs). ![Wix Logs window](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6f82b8af9105b4198ffe5babf7d6114d.png) 1. In the live site's home page or in Preview mode, click the button you created. The logs appear in the Wix Logs page: ![Wix Logs error example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/dbf2dc66af923e4d2c64564f8332de90.png) 1. Click **View** for the log with the error to see its full details on the right: ![Wix Logs log details](https://wixmp-833713b177cebf373f611808.wixmp.com/images/123b0719f98d5699cf242c68c1813b02.png) Check out the `jsonPayload` to see an explanation for the error. ## See also - [About Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md) - [View logs with Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-wix-logs.md) - [View logs with Google Cloud Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-google-cloud-logs.md) - [View logs with an external logging tool](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-an-external-logging-tool.md) - [Log object structure](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/log-object-structure.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Send Tracking and Analytics Events ## Article: Tutorial | Send Tracking and Analytics Events ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/monitoring/tutorial-send-tracking-and-analytics-events.md ## Article Content: # Tutorial | Send Tracking and Analytics Events You can add tracking code to monitor how site visitors interact with your site. This gives you insight into your site visitors' online behavior and can help you optimize your online marketing strategies. By the end of this tutorial, you'll have a site that sends custom events to an analytics tool (like Google Analytics) whenever a site visitor clicks the `download` button on your site. You'll use the following steps to send a tracking event from your page code: - [Connect your site to an analytics tool](#step-1--connect-an-analytics-tool) - [Set up a simple page](#step-2--set-up-your-page) - [Add code that sends a tracking event when a user performs a specific interaction](#step-3--add-tracking-code) > The code in this article was written using the following module versions: > > - @wix/site-window (v1.3.8) > - @wix/data (v1.0.241) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). > **Note** > > - Tracking & Analytics requires you to have an account with an external analytics tool and only works with premium sites. > - If your custom event isn't appearing in Google Analytics, you can use the [Google Tag Assistant](https://tagassistant.google.com/) to verify the event is being sent correctly. ## Step 1 | Connect an analytics tool Connect the desired tracking and analytics tool to your site. This tutorial connects to [Google Analytics](https://support.wix.com/en/article/tracking-events-on-your-wix-site-with-a-google-analytics-property), but the same concepts apply when connecting to [Meta Pixel](https://support.wix.com/en/article/connecting-a-facebook-pixel-and-the-conversions-api-to-your-wix-site-7351813). ## Step 2 | Set up your page 1. Add page elements. Add the following elements to your page: - A repeater to display a title and download button for each document. Set the repeater ID to `docRepeater`. - A text element (repeated) to display the document title. Set the text ID to `docTitle`. - A button (repeated) to download the document. Set the button ID to `downloadButton`. - A collection to store document data. Set the collection ID to `docData`. 2. Connect elements to your data. Query the `docData` collection and use the results to populate the repeater and its repeated elements. ```javascript import { items } from "@wix/data"; $w.onReady(async function () { const { items } = await items.query("docData").find(); $w("#docRepeater").data = items; $w("#docRepeater").onItemReady(($item, itemData) => { $item("#docTitle").text = itemData.title; }); }); ``` The result should look like this: ![Repeater with titles](https://wixmp-833713b177cebf373f611808.wixmp.com/images/38a99262e177152da44c3e763c6f4272.png) ## Step 3 | Add frontend code 1. Import the `window` module. Add this import statement at the top of your page code: ```javascript import { window } from "@wix/site-window"; ``` 2. Add an `onClick()` event to the repeated `download` button. Add an `onClick()` event handler to the repeater's `onReady()` handler: ```javascript $w("#docRepeater").onItemReady(($item, itemData) => { $item("#docTitle").text = itemData.title; $item("#downloadButton").onClick(async () => { // add the tracking code here in the next step }); }); ``` 3. Call the `trackEvent()` method. Add the tracking code inside the `onClick()` event. This sends Google Analytics a custom event that indicates a download occurred. When sending a custom event to Google Analytics, use these parameters: | Key | Value Type | Required | Usage | | --------------- | ---------- | -------- | --------------------------------------- | | `eventCategory` | string | yes | Object that was interacted with | | `eventAction` | string | yes | Type of interaction | | `eventLabel` | string | no | Event category | | `eventValue` | number | no | Numeric value associated with the event | ```javascript $item("#downloadButton").onClick(async () => { await window.trackEvent("CustomEvent", { event: "Document Download", eventCategory: "Downloads", eventAction: "Download", eventLabel: itemData.title, }); }); ``` Now, when a user clicks the download button, you'll see an event registered in your analytics tool. ## Complete code Here's the complete code for your page: ```javascript import { window } from "@wix/site-window"; import { items } from "@wix/data"; $w.onReady(async function () { const { items } = await items.query("docData").find(); $w("#docRepeater").data = items; $w("#docRepeater").onItemReady(($item, itemData) => { $item("#docTitle").text = itemData.title; $item("#downloadButton").onClick(async () => { await window.trackEvent("CustomEvent", { event: "Document Download", eventCategory: "Downloads", eventAction: "Download", eventLabel: itemData.title, }); }); }); }); ``` ## See also To learn more about the types of events you can send and their corresponding parameters, see [`trackEvent( )`](https://dev.wix.com/docs/sdk/frontend-modules/window/track-event.md) in the API Reference. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a One-Time Popup ## Article: Tutorial | Create a One-Time Popup ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-create-a-one-time-popup.md ## Article Content: # Tutorial | Create a One-Time Popup > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. In this tutorial, we demonstrate how to create a popup that appears only on a visitor's first page load during a browser session. We'll create the popup element and use the [Storage API](https://dev.wix.com/docs/sdk/frontend-modules/storage/storage/introduction.md) to track whether a visitor has already seen the popup in their current session. One-time popups are perfect for displaying important announcements, special offers, or welcome messages without repeatedly annoying visitors during their session. We'll use the following steps to build the one-time popup: - [Tutorial | Create a One-Time Popup](#tutorial--create-a-one-time-popup) - [Before you begin](#before-you-begin) - [Step 1 | Set up the popup](#step-1--set-up-the-popup) - [Step 2 | Implement the popup display and storage functionality](#step-2--implement-the-popup-display-and-storage-functionality) - [Complete code example](#complete-code-example) - [See also](#see-also) > **Note:** The code in this article was written using the following module versions: > > - @wix/site-storage (v1.0.0) > - @wix/site-window (v1.0.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Before you begin It's important to note the following points before doing this tutorial: - This tutorial uses session storage, but you can optionally switch to [local storage](https://dev.wix.com/docs/sdk/frontend-modules/storage/introduction.md). - The code should be added to your site's **masterpage.js** file so that it works across all of your site pages. ## Step 1 | Set up the popup In this step, you'll configure the popup element that will serve as the popup on your site. To set up your popup: 1. Add a popup to your site through the **Add** menu if you haven't already. 2. Select the popup element and click the **Set Triggers** button. 3. In **Popup Settings**, configure the popup so that it doesn't automatically display on pages. 4. Give your popup a meaningful name, such as **Announcement**. We'll use this name in the code. 5. Design the popup content to fit your needs. You can add, delete, and modify any elements within the popup. ## Step 2 | Implement the popup display and storage functionality In this step, you'll implement the code for tracking and displaying the popup. 1. Add the following import statements at the top of your **masterPage.js** file: ```javascript import { session } from "@wix/site-storage"; import { window } from "@wix/site-window"; ``` > **Note:** Adding our code to the **masterPage.js** file ensures the popup appears immediately when a visitor first arrives at your site, regardless of which page they land on. To show the popup only on specific pages, add the code to those individual page files instead. 2. Add the main popup logic using the `onReady()` event handler. When the page is ready, check session storage for `firstTimePopupShown`. If it's missing, open the "Announcement" popup and set the key so that the popup doesn't open again. ```javascript $w.onReady(async function () { if (!(await session.getItem("firstTimePopupShown"))) { window.openLightbox("Announcement"); session.setItem("firstTimePopupShown", "yes"); } }); ``` > **Note:** This tutorial uses session storage, so the popup appears once per browsing session. To display the popup only once ever, use local storage instead. Change the import statement from `session` to `local` and update all instances of `session` to `local` in your code. ## Complete code example ```javascript import { session } from "@wix/site-storage"; import { window } from "@wix/site-window"; $w.onReady(async function () { // Check if the lightbox (popup) has been shown before if (!(await session.getItem("firstTimePopupShown"))) { // Open the lightbox (popup) window.openLightbox("Announcement"); // Mark that the lightbox (popup) has been shown session.setItem("firstTimePopupShown", "yes"); } }); ``` ## See also - [Using Popups](https://support.wix.com/en/article/studio-editor-using-popups) - [Storage API](https://dev.wix.com/docs/sdk/frontend-modules/storage/introduction.md) - [Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Customize a Wix Form ## Article: Tutorial | Customize a Wix Form ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-customize-a-wix-form.md ## Article Content: # Tutorial | Customize a Wix Form When you use the [Wix Forms app](https://support.wix.com/en/article/about-wix-forms) to create a form on your site, it comes with built-in functionalities. However, you might still want to customize the form further. In this tutorial, you'll learn how to customize a Wix Form using the JavaScript SDK. You'll display a custom thank you message using user input from the form submission. You'll complete the following steps to customize your form: 1. [Set up a Wix Form on your site](#step-1-add-and-set-up-a-wix-form) 2. [Display a personalized thank you message](#step-2-display-a-personalized-thank-you-message) ## Step 1: Add and set up a Wix Form 1. [Add and set up a Wix Form](https://support.wix.com/en/article/adding-and-setting-up-a-form-on-your-site) on your site in the Wix Editor. 2. Configure your form with the following fields: - **First Name** (text input) - **Last Name** (text input) - **Donation Amount** (number input) 3. Change the form's default ID to `myForm`. You'll use this ID to reference the form in your code in the next step. ## Step 2: Display a personalized thank you message In this step, you'll add code to display a custom thank you message when a form is submitted. It'll include personalized information from the form elements. 1. Add an empty [text element](https://support.wix.com/en/article/studio-editor-adding-and-customizing-text) with the ID `thankYouText` to your site to display the thank you message. 2. Set up the form submit success handler and get form field values. ```javascript $w("#myForm").onSubmitSuccess(() => { const formData = $w("#myForm").getFieldValues(); // Code in next step goes here... }); ``` 3. Extract specific field values from the form data. ```javascript // ...Code from previous step const firstName = formData.first_name; const lastName = formData.last_name; const donation = formData.donation; // Code in next step goes here... ``` 4. Display the personalized thank you message: ```javascript // ...Code from previous step $w( "#thankYouText" ).text = `Thank you ${firstName} ${lastName}, for your generous donation of $${donation}!`; ``` ## Full code ```javascript $w("#myForm").onSubmitSuccess(() => { const formData = $w("#myForm").getFieldValues(); const firstName = formData.first_name; const lastName = formData.last_name; const donation = formData.donation; $w( "#thankYouText" ).text = `Thank you ${firstName} ${lastName}, for your generous donation of $${donation}!`; }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Display Elements in Mobile Only ## Article: Tutorial | Display Elements in Mobile Only ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-display-elements-in-mobile-only.md ## Article Content: # Tutorial | Display Elements in Mobile Only > The code in this article was written using the following module versions: > > - @wix/site-window (v1.38.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). You can use code to control elements so they act or display differently in the mobile version of your site. ## Step 1 | Add the elements on your site Add elements to your site that you'd like to customize for the mobile version. ## Step 2 | Open the page code file Open the code file for your page where you added the elements. The way you open the file depends on the IDE you're using. ::::tabs :::Editor 1. Navigate to **Page Code** in the Code sidebar. 1. Select your page. A page code file opens. The file includes a sample `onReady()` function. ::: :::Wix-IDE-or-Local-IDE Open the file in the `Pages` folder. The file includes a sample `onReady()` function. ::: :::: ## Step 3 | Check the device type Check whether your site is being viewed on a mobile device. 1. In the first line of your code, import the [@wix/site-window](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md) API. It lets you determine the type of device your site is being viewed on. ```js import { window } from "@wix/site-window"; ``` Learn more about using [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) when developing sites. 1. Wrap the code for the element in an `if` statement to handle the various devices on which the element exists. ```js $w.onReady(async function () { if ( (await window.formFactor()) === "Mobile" || (await window.formFactor()) === "Tablet" ) { } else { } }); ``` ## Step 4 | Customize actions on elements In the conditional statement write any actions you’d like to do with your elements in the mobile version. For example, here is code for hiding a button when viewed on a desktop screen, but showing the button on mobile and tablet screens: ```js import { window } from "@wix/site-window"; $w.onReady(async function () { if ( (await window.formFactor()) === "Mobile" || (await window.formFactor()) === "Tablet" ) { $w("#button1").show(); } else { $w("#button1").hide(); } }); ``` ## Step 5 | Test If you want to test how your code works on mobile devices, publish your site and view the published version on a mobile device or in a mobile device emulator.
**Important:** If you preview your site, it will always behave as if it's being viewed on a desktop device, even if you preview from the mobile editor.
To test your site on a desktop machine as if it's being viewed on a mobile device: 1. Publish your site. 1. View the published site. 1. Open your browser's [developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools). 1. Use your browser's developer tools to emulate a mobile device. For example, for Google Chrome, this is called **Toggle device toolbar** and turned on using this icon ![toggle device toolbar](https://wixmp-833713b177cebf373f611808.wixmp.com/images/70adbe3c880a3585698dcb51ddc466e3.png). 1. Refresh the page so your site now loads as if it were on a mobile device. ## See also - [About Mobile Sites with Code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/mobile/about-mobile-sites-with-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Hide a Video Player When There's No Video ## Article: Tutorial | Hide a Video Player When There's No Video ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-hide-a-video-player-when-there-s-no-video.md ## Article Content: # Tutorial | Hide a Video Player When There's No Video Learn how to automatically hide a [video player](https://dev.wix.com/docs/velo/velo-only-apis/$w/video-player/introduction.md) element when the connected collection item has no video URL, instead of showing a default placeholder. This is useful when the page shows various collection items that contain multiple fields, and may or may not include a video. In this tutorial we create a collection of minimal items, displayed in a [repeater](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/introduction.md). We'll use the following steps to create this functionality: 1. [Create a collection.](#step-1--create-a-collection) 2. [Set up page elements.](#step-2--set-up-page-elements) 3. [Add code.](#step-3--add-code) ## Step 1 | Create a collection 1. Create a collection with a URL type field. Name the field `url`. 2. Add to the collection at least 1 item with a valid video URL in the `url` field, and at least 1 item with an empty `url` field. ## Step 2 | Add elements to your page 1. Add a repeater to your page. Set the repeater ID to `repeater`. 2. [Connect](https://support.wix.com/en/article/about-datasets-and-connecting-data) the repeater to the collection. 3. Add a video player element to the repeater items. 4. Connect the video player to the collection. 5. [Connect](https://support.wix.com/en/article/cms-formerly-content-manager-displaying-collection-content-in-the-single-video-player) the video player's **Video source** to the `url` field. ## Step 3 | Add frontend code Add this code to your page to wait for the repeater items to load, and hide the video player in items that contain no video URL: ```javascript $w("#repeater").onItemReady(($item, itemData) => { if (!itemData.url) { $item("#videoPlayer").collapse(); } }); ``` ## Test your implementation In edit mode, video players will show some default content even if they don't contain a video. To test your implementation, preview your site. In items with an empty `url` field, the video player will automatically collapse. ## See also - [About Collections](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md) - [Video Player API](https://dev.wix.com/docs/velo/velo-only-apis/$w/video-player/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Use the Upload Button with Code ## Article: Tutorial | Use the Upload Button with Code ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-use-the-upload-button-with-code.md ## Article Content: # Tutorial | Use the Upload Button with Code In this tutorial, we demonstrate how to create a file upload form that allows site visitors to upload files to your site's Media Manager using the JavaScript SDK. Site visitors click the upload button to choose a file and then click the regular button to perform the upload.
**Tip:** For more complex file upload scenarios, you can use the [Media API](https://dev.wix.com/docs/sdk/backend-modules/media/introduction.md).
We'll use the following steps to build the file upload functionality: 1. Add page elements. 2. Add code to handle file selection and upload. ## Step 1 | Add elements to your page Add the following elements to your page: - An [upload button](https://dev.wix.com/docs/velo/velo-only-apis/$w/upload-button/introduction.md) for choosing a file. Set the upload button ID to `uploadButton`. - A regular [button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) to trigger the upload code. Set the button ID to `button`. - A [text](https://dev.wix.com/docs/velo/velo-only-apis/$w/text/introduction.md) element to display messages to site visitors. Set the text ID to `text`. - An [image](https://dev.wix.com/docs/velo/velo-only-apis/$w/image/introduction.md) element. Any image is fine. We're only using it as a placeholder for the uploaded image. Set the image ID to `image`. The form should look like this:
![File upload form with progress feedback](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f5468bf0682ef0ecc9d9e54d74052a37.png)
## Step 2 | Add frontend code The upload button handles site visitors choosing the file. We'll add an `onClick()` function to make the regular button perform the actual upload. To create the `onClick()` function: 1. In the page code panel, add an `onClick()` event handler for the button: ```javascript $w("#button").onClick(async () => { // Code in the next steps goes here. }); ``` 2. Start the handler function with a check to see if the visitor has chosen a file with the upload button. If not, prompt the visitor to choose a file: ```javascript if ($w("#uploadButton").value.length > 0) { // Code in the next steps goes here. } else { $w("#text").text = "Choose a file to upload."; } ``` 3. Continue the handler function code for when a file is selected. Display feedback text, upload the selected file by calling the `uploadFiles()` async method, and then display the uploaded image and an appropriate message: ```javascript $w("#text").text = "Uploading " + $w("#uploadButton").value[0].name; try { const uploadedFiles = await $w("#uploadButton").uploadFiles(); $w("#text").text = "File uploaded successfully"; $w("#image").src = uploadedFiles[0].fileUrl; } // Code in the next step goes here. ``` 4. Handle failed uploads by displaying an appropriate message: ```javascript catch { $w("#text").text = "Upload failed. Try again or choose a different file."; } ``` ## Complete code example Here's the complete code for your page: ```javascript $w("#button").onClick(async () => { if ($w("#uploadButton").value.length > 0) { $w("#text").text = "Uploading " + $w("#uploadButton").value[0].name; try { const uploadedFiles = await $w("#uploadButton").uploadFiles(); $w("#text").text = "File uploaded successfully"; $w("#image").src = uploadedFiles[0].fileUrl; } catch { $w("#text").text = "Upload failed. Try again or choose a different file."; } } else { $w("#text").text = "Choose a file to upload."; } }); ``` ## See also - [Upload button](https://dev.wix.com/docs/velo/velo-only-apis/$w/upload-button/introduction.md) - [Supported media files](https://support.wix.com/en/article/supported-media-file-types-and-file-sizes) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Work with Multi-State Boxes ## Article: Tutorial | Work with Multi-State Boxes ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-work-with-multi-state-boxes.md ## Article Content: # Tutorial | Work with Multi-State Boxes A [multi-state box](https://dev.wix.com/docs/velo/velo-only-apis/$w/multi-state-box/introduction.md) contains multiple states with different content, and displays a single state at a time. Each state corresponds to a specific situation or status. You need to add code to your site to define when each state is displayed. This article demonstrates how to set up your multi-state box using code. ## Set up your multi-state box To set up your multi-state box, follow this general procedure: 1. Add a multi-state box to your page. 2. Set up the multi-state box's states. 3. Add code to define when each state will be displayed: 1. Define a condition (for example, a site visitor clicked a button, a product is out of stock). 2. Select your multi-state box using its ID. 3. Apply the [`changeState()`](https://www.wix.com/velo/reference/$w.MultiStateBox.html#changeState) function with the state you want to move to. ## Example 1: Site visitors change the state This example shows how to set up your multi-state box so that site visitors can switch between states by clicking a button and then focus on the button in the currently displayed state to enhance keyboard and screen reader functionality for visitors with disabilities. We added a multi-state box to our page called `myStateBox`, with 2 states called `state1` and `state2`. Then we added a button to each state for switching between the states. > **Note** > You can view and change your multi-state box and state IDs in the [Properties & Events panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md). ### State 1 ::::tabs :::Wix-Editor ![state1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7752a1ac284983f8cbfa1db804b7b048.png) ::: :::Wix-Studio ![state2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3178f431f403ecad057a52a7c1dc6c73.png) ::: :::: - `myStateBox`: ID of my multi-state box. - `state1`: ID of the current state, State 1. - `button1`: ID of button to click to move to State 2. ### State 2 ::::tabs :::Wix-Editor ![state1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/63b0b1f87233e5336d19c8545da8563f.png) ::: :::Wix-Studio ![state2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0f9601371fc7d40caea115f580d0b852.png) ::: :::: - `myStateBox`: ID of my multi-state box. - `state2`: ID of the current state, State 2. - `button2`: ID of button to click to move to State 1. ### Code Add the following code to your page. This code sets up click handlers for both buttons that switch between states and focus on the appropriate button in each state: ```javascript $w.onReady(function () { $w("#button1").onClick(() => { $w("#myStateBox").changeState("state2"); $w("#button2").focus(); }); $w("#button2").onClick(() => { $w("#myStateBox").changeState("state1"); $w("#button1").focus(); }); }); ``` ### Adapt this scenario You can adapt this scenario to enable site visitors to: - Switch between a brief description and a detailed block of text. - Fill out a custom multi-step form. - Navigate what appear to be multiple tabs. ## Example 2: State per status > **Note:** > The images in this example show the multi-state box in the Wix Editor. The multi-state box in Wix Studio functions similarly to Wix Editor, with the only difference being its appearance, as demonstrated in the example above. This example shows how to set up your multi-state box with code so that a different state is displayed depending on a particular status. The example uses Wix Store products, but you can adapt the example for other scenarios. We added a multi-state box to our Wix Stores [product page](https://support.wix.com/en/article/customizing-your-wix-stores-product-page) that displays a different badge depending on the product's status. We added 3 states to our multi-state box: - Out of Stock - On Sale - Featured ### State 1: Out of stock
!["Out of Stock" badge state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8fda887037df964c06c9b8a9a0daeaeb.png)
- `badgeStatebox`: ID of my multi-state box. - `outOfStock`: ID of the current state. For products that are out of stock. ### State 2: On sale !["On Sale" badge state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a9c0b02dc533fb8e1a0799cfd23ca60e.png) - `badgeStatebox`: ID of my multi-state box. - `onSale`: ID of the current state. For products that are on sale. ### State 3: Featured
!["Featured" badge state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4140c40182c7a54879d704c27e136b39.png)
- `badgeStatebox`: ID of my multi-state box. - `featured`: ID of the current state. For products that aren't on sale or out of stock. ### Code Add the following code to your [product page](https://www.wix.com/velo/reference/$w.ProductPage.html). This code listens for product changes and displays the appropriate badge state based on the product's availability and pricing: ```javascript import { location } from "@wix/site-location"; $w.onReady(function () { location.onChange(() => { $w("#myProductPage") .getProduct() .then((product) => { if (!product.inStock) { $w("#badgeStatebox").changeState("outOfStock"); } else if (product.price !== product.discountedPrice) { $w("#badgeStatebox").changeState("onSale"); } else { $w("#badgeStatebox").changeState("featured"); } }) .catch((error) => { console.log(error); }); }); }); ```
**Tip** If you want some products not to display a badge at all, you could [hide()](https://www.wix.com/velo/reference/$w.MultiStateBox.html#hide) the multi-state box when particular conditions are met.
### Adapt this scenario You can adapt this scenario to: - Show different site content if a shopping cart is empty or full. - Display an error state when something goes wrong. - Show different site content depending on whether there's an upcoming event. - Display a preloader while the page is loading. ## See also - [$w.MultiStateBox API](https://www.wix.com/velo/reference/$w.MultiStateBox.html) - [$w.State API](https://www.wix.com/velo/reference/$w.State.html) - [About Multi-State Boxes](https://dev.wix.com/docs/velo/velo-only-apis/$w/multi-state-box/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Staff Vacation Management Interface ## Article: Tutorial | Create a Staff Vacation Management Interface ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/bookings/tutorial-create-a-staff-vacation-management-interface.md ## Article Content: # Tutorial | Create a Staff Vacation Management Interface Using the [Bookings APIs](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md) and [Calendar APIs](https://dev.wix.com/docs/api-reference/business-management/calendar/introduction.md), you can create a backend interface where [staff members](https://support.wix.com/en/article/about-staff-members-in-wix-bookings) can block their vacation time. This tutorial shows you how to build a [dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md) where [site collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) select a staff member, choose vacation dates, and block that time in the [Bookings calendar](https://support.wix.com/en/article/wix-bookings-about-the-wix-booking-calendar). During those periods, the staff member is unavailable for bookings. ## How staff members, resources, and schedules interact To schedule staff vacations, you need to understand how Wix Bookings manages staff members, resources, and calendars. Wix Bookings automatically creates and manages an associated [resource](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/introduction.md) for each staff member. Each resource has an associated [schedule](https://dev.wix.com/docs/api-reference/business-management/calendar/schedules-v3/introduction.md), which Wix Bookings uses to manage that resource's availability. The entries on the schedule are [events](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/introduction.md), during which the resource can't be booked. Therefore, to schedule a staff member's vacation time, you need the full resource object associated with that staff member, which contains the ID of the events schedule associated with that resource. To [create the vacation event](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/create-event.md), you must specify that schedule ID. Learn more about [scheduling and availability in Wix Bookings](https://dev.wix.com/docs/api-reference/business-solutions/bookings/introduction.md#scheduling-and-availability) and how [Wix Bookings uses the Calendar APIs](https://dev.wix.com/docs/api-reference/business-management/calendar/wix-bookings-integration.md). ## What you'll build By the end of this tutorial, you'll have a [dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md) that: - Displays a dropdown list of all staff members. - Lets site collaborators choose vacation start and end dates. - Creates blocked time events on the staff member's calendar. - Automatically prevents bookings during blocked vacation periods. > The code in this article was written using the following module versions: > > - @wix/bookings (v1.0.1167) > - @wix/web-methods (v1.0.0) > - @wix/calendar (v1.0.185) > - @wix/essentials (v1.0.0) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/work-with-npm-packages-with-the-wix-cli.md). Use the following steps to build the functionality: 1. [Create backend methods for staff availability management.](#step-1--create-backend-methods-for-staff-availability-management) 2. [Create the frontend interface.](#step-2--create-the-frontend-interface) ## Before you begin It's important to note the following points before starting to code: - The Wix user must install the [Wix Bookings](https://www.wix.com/app-market/web-solution/bookings) app on their site. - The user must [add at least one staff member](https://support.wix.com/en/article/wix-bookings-adding-staff-members) in their Wix Bookings settings. ## Step 1 | Create backend methods for staff availability management Create backend methods to query staff members and create vacation events on their schedules: 1. In your site's [backend folder](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/about-the-site-backend.md), create a new file called `staff-management.web.js`. 2. Add the following import statements: ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import { staffMembers } from "@wix/bookings"; ``` 3. Create a [web method](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction.md) to query all staff members. Specify `RESOURCE_DETAILS` in the optional `fields` array to retrieve the full resource object. It contains the schedule ID needed to create vacation events: ```javascript export const queryStaff = webMethod(Permissions.SiteMember, async () => { try { const { items } = await staffMembers .queryStaffMembers({ options: { fields: ["RESOURCE_DETAILS"] } }); return items; } catch (error) { console.error("Failed to query staff members:", error); throw error; } }); ``` 4. Create a second backend file called `event-management.web.js` to manage vacation events. 5. Add the following import statements to this file: ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import { events } from "@wix/calendar"; import { auth } from "@wix/essentials"; ``` 6. Create a web method to set up a staff member's vacation. This method receives the staff member details and date range, constructs an event object, and creates a blocked calendar event: ```javascript export const createVacationEvent = webMethod( Permissions.SiteMember, async (member, startDate, endDate) => { // Use the schedule ID from the staff member's associated resource to create the vacation event const vacationEvent = { scheduleId: member.resource.eventsSchedule._id, title: `${member.name} is on holiday!`, start: { localDate: `${startDate}T00:00:00`, }, end: { localDate: `${endDate}T23:59:59`, }, resource: [member.resource], }; try { const elevatedCreateEvent = auth.elevate(events.createEvent); const createdEvent = await elevatedCreateEvent(vacationEvent); return createdEvent; } catch (error) { console.error( `Failed to create vacation event for ${member.name}:`, error ); throw new Error(`Failed to create vacation: ${error.message}`); } } ); ``` These backend methods allow site members to query staff and create vacation events on their schedules. ## Step 2 | Create the frontend interface This step creates a dashboard page interface that allows site collaborators to select a staff member, choose vacation dates, and create blocked time events. To build the frontend interface: 1. [Add a dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/add-a-dashboard-page.md) to your site. 2. Add the following elements to the dashboard page: - A dropdown element with ID **staffDropdown** for selecting staff members. - A date picker with ID **datePickerStart** for selecting the vacation start date. - A date picker with ID **datePickerEnd** for selecting the vacation end date. - A button with ID **setupButton** to trigger the vacation setup. - A text element with ID **statusText** to display status messages. Your dashboard page should look like this: ![Staff members dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/defe7cdf4b85a969da1ad6b20c8fee1f.png) 3. In your dashboard page's code editor, add the following code to import the backend methods and initialize a variable to store the queried staff members: ```javascript import { queryStaff } from "backend/staff-management.web.js"; import { createVacationEvent } from "backend/event-management.web.js"; let staffMembers; ``` 4. Add methods to format dates and set up the details of the vacation event: ```javascript // Helper method to format dates into YYYY-MM-DD format required by the Wix Calendar API function formatDate(dateObj) { const date = String(dateObj.getDate()).padStart(2, "0"); const month = String(dateObj.getMonth() + 1).padStart(2, "0"); const year = dateObj.getFullYear(); return `${year}-${month}-${date}`; } async function setUpVacation(startDateObj, endDateObj) { try { await $w("#setupButton").disable(); const selectedIndex = $w("#staffDropdown").selectedIndex; const staffName = $w("#staffDropdown").options[selectedIndex].label; $w("#statusText").text = `Setting up vacation for ${staffName}...`; // Find the selected staff member const selectedMemberId = $w("#staffDropdown").options[selectedIndex].value; const selectedMember = staffMembers.find( (member) => member._id === selectedMemberId ); // Format the selected dates const startDate = formatDate(startDateObj); const endDate = formatDate(endDateObj); // Create vacation event await createVacationEvent(selectedMember, startDate, endDate); $w("#statusText").text = `${staffName} has scheduled their vacation.`; await $w("#setupButton").enable(); } catch (error) { console.error("Failed to setup vacation: ", error); $w("#statusText").text = "Failed to schedule vacation."; await $w("#setupButton").enable(); } } ``` 5. Add a method to query staff members and populate the dropdown menu: ```javascript async function populateStaffDropdown() { try { staffMembers = await queryStaff(); const staffOptions = staffMembers.map((member) => ({ label: member.name, value: member._id, })); $w("#staffDropdown").options = staffOptions; $w("#staffDropdown").selectedIndex = 0; $w("#statusText").text = "Select vacation dates and click Set Up Vacation"; await $w("#setupButton").enable(); } catch (error) { console.error("Failed to load staff members: ", error); $w("#statusText").text = "Failed to load staff members"; } } ``` 6. Set up the dashboard page interface: ```javascript async function setUpPage() { let startDate; let endDate; // Set up date pickers $w("#datePickerStart").dateFormat = "YYYY/MM/DD"; $w("#datePickerStart").value = new Date(); $w("#datePickerEnd").dateFormat = "YYYY/MM/DD"; $w("#datePickerEnd").value = new Date(); // Register event handlers $w("#datePickerStart").onChange(async (event) => { await $w("#setupButton").enable(); startDate = event.target.value; }); $w("#datePickerEnd").onChange(async (event) => { await $w("#setupButton").enable(); endDate = event.target.value; }); // Set up the setup button $w("#setupButton").onClick(() => { setUpVacation(startDate, endDate); }); $w("#staffDropdown").onChange(async () => { await $w("#setupButton").enable(); }); // Disable setup button until dropdown is populated await $w("#setupButton").disable(); // Populate the dropdown menu with staff members populateStaffDropdown(); } ``` 7. In the page's [`onReady()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md) method, set up the dashboard page: ```js $w.onReady(function () { setUpPage(); }); ``` When a site collaborator chooses vacation dates and clicks the setup button, the code creates a blocked event in the Bookings Calendar. ![Blocked Vacation Time on Bookings Calendar](https://wixmp-833713b177cebf373f611808.wixmp.com/images/36ad82b0449d7d628aa9bd76164188f4.png) ## Complete code Here is the complete tutorial code: ### Backend code Here are the complete backend files: #### staff-management.web.js ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import { staffMembers } from "@wix/bookings"; export const queryStaff = webMethod(Permissions.SiteMember, async () => { try { const { items } = await staffMembers .queryStaffMembers({ options: { fields: ["RESOURCE_DETAILS"] } }); if (!items || items.length === 0) { throw new Error("No staff members found"); } return items; } catch (error) { console.error("Failed to query staff members:", error); throw error; } }); ``` #### event-management.web.js ```javascript import { Permissions, webMethod } from "@wix/web-methods"; import { events } from "@wix/calendar"; import { auth } from "@wix/essentials"; export const createVacationEvent = webMethod( Permissions.SiteMember, async (member, startDate, endDate) => { const vacationEvent = { scheduleId: member.resource.eventsSchedule._id, title: `${member.name} is on holiday!`, start: { localDate: `${startDate}T00:00:00`, }, end: { localDate: `${endDate}T23:59:59`, }, resource: [member.resource], }; try { const elevatedCreateEvent = auth.elevate(events.createEvent); const createdEvent = await elevatedCreateEvent(vacationEvent); return createdEvent; } catch (error) { console.error(`Failed to create vacation event:`, error); throw new Error(`Failed to create vacation: ${error.message}`); } } ); ``` ### Dashboard page code Here's the complete dashboard page code: ```javascript import { queryStaff } from "backend/staff-management.web.js"; import { createVacationEvent } from "backend/event-management.web.js"; let staffMembers; // Helper method to format dates into YYYY-MM-DD format required by the Wix Calendar API function formatDate(dateObj) { const date = String(dateObj.getDate()).padStart(2, "0"); const month = String(dateObj.getMonth() + 1).padStart(2, "0"); const year = dateObj.getFullYear(); return `${year}-${month}-${date}`; } async function setUpVacation(startDateObj, endDateObj) { try { await $w("#setupButton").disable(); const selectedIndex = $w("#staffDropdown").selectedIndex; const staffName = $w("#staffDropdown").options[selectedIndex].label; $w("#statusText").text = `Setting up vacation for ${staffName}...`; // Find the selected staff member const selectedMemberId = $w("#staffDropdown").options[selectedIndex].value; const selectedMember = staffMembers.find( (member) => member._id === selectedMemberId ); // Format the selected dates const startDate = formatDate(startDateObj); const endDate = formatDate(endDateObj); // Create vacation event await createVacationEvent(selectedMember, startDate, endDate); $w("#statusText").text = `${staffName} has scheduled their vacation.`; await $w("#setupButton").enable(); } catch (error) { console.error("Failed to setup vacation: ", error); $w("#statusText").text = "Failed to schedule vacation."; await $w("#setupButton").enable(); } } async function populateStaffDropdown() { try { staffMembers = await queryStaff(); const staffOptions = staffMembers.map((member) => ({ label: member.name, value: member._id, })); $w("#staffDropdown").options = staffOptions; $w("#staffDropdown").selectedIndex = 0; $w("#statusText").text = "Select vacation dates and click Set Up Vacation"; await $w("#setupButton").enable(); } catch (error) { console.error("Failed to load staff members: ", error); $w("#statusText").text = "Failed to load staff members."; } } async function setUpPage() { let startDate; let endDate; $w("#datePickerStart").dateFormat = "YYYY/MM/DD"; $w("#datePickerStart").value = new Date(); $w("#datePickerEnd").dateFormat = "YYYY/MM/DD"; $w("#datePickerEnd").value = new Date(); $w("#datePickerStart").onChange(async (event) => { await $w("#setupButton").enable(); startDate = event.target.value; }); $w("#datePickerEnd").onChange(async (event) => { await $w("#setupButton").enable(); endDate = event.target.value; }); $w("#setupButton").onClick(() => { setUpVacation(startDate, endDate); }); $w("#staffDropdown").onChange(async () => { await $w("#setupButton").enable(); }); await $w("#setupButton").disable(); populateStaffDropdown(); } $w.onReady(function () { setUpPage(); }); ``` ## See also - [About in Wix Bookings APIs](https://dev.wix.com/docs/api-reference/business-solutions/bookings/introduction.md) - [Bookings: Staff Members API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/staff-members/staff-members/introduction.md) - [Bookings: Resources API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/introduction.md) - [Calendar Events API](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/introduction.md) - [About Dashboard Pages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md) - [Tutorial: Block Off Time in the Bookings Calendar](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/bookings/tutorial-block-off-time-in-the-bookings-calendar.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About JavaScript Support ## Article: About JavaScript Support ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-java-script-support.md ## Article Content: # About JavaScript Support Wix supports working in JavaScript to develop websites, as well as some special features, including: - Support for JavaScript features - Support for modules - Support for the JavaScript Fetch API - Sharing JavaScript code naturally between the backend and frontend using [web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) ## Supported IDEs You can work in JavaScript in: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor). ## JavaScript feature support (ECMAScript 2020) Wix supports modern JavaScript features up through and including the [ES2020](https://tc39.es/ecma262/2020/) standard. You can use promises, async/await, and arrow functions, which were introduced with ES2017. Browsers are gradually adopting the ES2019 standard. Until these standards are fully implemented, Wix transpiles your code into ES5, so it can run in current browsers. Wix supports [source maps](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/SourceMap), so that even though the browser runs transpiled ES5 code, you can debug your ES2019 source code in your browser's developer tools. ## Module support (ECMAScript 2015) Wix supports the native module functionality included in the ES2015 release of JavaScript. To use ES2015 module functionality, you need to follow the [ES2015 Module Syntax](http://www.2ality.com/2014/09/es6-modules-final.html). Only those items specifically exported in a module are exposed to other files. All other items in your module are internal to the module only. For examples of how to use this functionality, see MDN's [JavaScript modules guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). > **Notes:** > > - You can only export functions in files that are located in the Public or Backend sections of your site's code. You can't export functions from page or popup files. > - Certain module export formats aren't supported in `events.js`, `data.js`, or `routers.js` files. For more information, see [Module Export Syntax](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/module-export-syntax.md). ### Module scope The following is a list of guidelines that define how you can share modules and functions between, and in, the backend and public scopes: - A JavaScript file or script in backend can import a module from any file in backend or public. - A file in public can import a module from any file in public. - You can import functions from backend and use them in public, using a [web module](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md). - You can use relative paths to refer to files with the "." prefix. - You can import a module from backend with the _backend/_ prefix. - You can import a module from public with the _public/_ prefix. - Modules can import other modules. ## See also - [About Promises](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-promises.md) - [Module export syntax](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/module-export-syntax.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Module Export Syntax ## Article: Module Export Syntax ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/module-export-syntax.md ## Article Content: # Module Export Syntax Wix supports working in JavaScript to develop websites, as well as [support for modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-java-script-support.md#module-support-ecmascript-2015). This allows you to export modules from your code files and import them into other code files. This article explains the supported syntax for exporting modules. ## Supported export syntax When exporting functions in the following files, use the standard export syntax contained in the documentation's code examples: - [`events.js`](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/define-backend-event-handlers.md) - [`data.js`](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md#hook-conventions) - [`routers.js`](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) - [Service plugin code files](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/automations/about-custom-actions.md) You can also use other export formats that are part of the [ES6 (ES2015) module export syntax](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export). However, the following export formats are **not** supported in `events.js`, `data.js`, `routers.js`, or service plugin code files: - **Re-exporting a function:** Directly re-exporting a function imported from an npm or Velo package. For example, this **doesn't** work: ```javascript import { myFunction } from "myPackage"; export const wixMediaManager_onFileUploaded = myFunction; ``` The following alternative syntax **is** supported: ```javascript import { myFunction } from "myPackage"; export const wixMediaManager_onFileUploaded = (paramA, paramB) => myFunction(paramA, paramB); ``` - **Dynamic exports:** Exports including a dynamic operation or expression that requires execution to resolve. For example, this **doesn't** work: ```javascript import myPackage from "myPackage"; export const myRouter_Router = myPackage.router((paramA, paramB) => { // ... }); ``` The following alternative syntax **is** supported: ```javascript import myPackage from "myPackage"; const functionForExport = myPackage.router((paramA, paramB) => { // ... }); export const myRouter_Router = (paramA, paramB) => functionForExport(paramA, ParamB); ``` - **Exports wrapped in objects:** Exports where functions are defined as properties of an object. For example, this **doesn't** work: ```javascript const dataHooks = { beforeInsert: (paramA, paramB) => { // ... }, }; export const MyCollection_beforeInsert = dataHooks.beforeInsert; ``` The following alternative syntax **is** supported: ```javascript const dataHooks = { beforeInsert: (paramA, paramB) => { // ... }, }; export const MyCollection_beforeInsert = (paramA, paramB) => dataHooks.beforeInsert(paramA, paramB); ``` - **CommonJS exports:** Exports in the CommonJS (CJS) format that preceded ES6 (ES2015). For example, this **doesn't** work: ```javascript module.exports = { MyCollection_beforeInsert: () => {}, }; ``` ## See also - [About JavaScript support](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-java-script-support.md) - [About the Wix JavaScript SDK](https://dev.wix.com/docs/sdk/articles/get-started/about-the-wix-java-script-sdk.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Promises ## Article: About Promises ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-promises.md ## Article Content: # About Promises > **Note:** This article is intended for coders with limited JavaScript experience, and serves as an introduction to this basic JavaScript content. Some of the code you write when developing with the Wix JavaScript SDK is asynchronous code. Asynchronous code doesn't execute in the order it's written, unlike synchronous code. Instead, it returns a promise. A [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. While the promise is pending, synchronous code continues to run. When developing your website, there are some cases where you need to work with promises. For example: - [Calling backend functions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/call-backend-code-from-the-frontend.md) from your page code. - Calling SDK APIs that return promises. Promises are a regular JavaScript concept, so it's easy to use AI chatbots or search engines to troubleshoot. ## Handle a promise Handling a promise means telling your code to read and use the value of the promise only once its resolved value is known. This means the asynchronous code has finished running. At any given time, a promise has one of the following statuses: - **Pending**: The asynchronous code is still running. - **Fulfilled**/**resolved**: The asynchronous code has finished running and has successfully returned a value. - **Rejected**: The asynchronous code has finished running and has thrown an error. You can tell your code to expect a promise and to wait for the asynchronous code to finish running in the following ways: - [`async`/`await`](#asyncawait): Allows you to treat asynchronous code as if it's synchronous, meaning the next line of code won't run until the promise is resolved. - [`then()`](#then): Allows you to run other synchronous code while your asynchronous code is running. You have more control over when your code runs, but it's less readable. ## `async`/`await` The `async`/`await` syntax allows you to handle promises in a way that makes your asynchronous code appear synchronous. This can make your code more readable, as the next line of code won't run until the promise is resolved. ### Define asynchronous functions Asynchronous functions are functions that contain asynchronous code. An asynchronous function's return value is always a promise. To define a function as asynchronous, add `async` before the function declaration. For example: ```js async function myFunction() {} const myFunction = async () => {}; ``` > **Note:** This method isn't supported at the top level, because top level code is synchronous. Add `await` before the call to an asynchronous function to make your code wait for the function to resolve before moving on to the next line. For example: ```js const myPromiseValue = await myAsyncFunction(); ``` Then you can use the value of the resolved promise on the next line. For example: ```js async function myFunction() { const myPromiseValue = await myAsyncFunction(); console.log(myPromiseValue); } ``` ### Handle errors with `async`/`await` If your promise is rejected, something has gone wrong in the asynchronous code and you need to handle the error. Use the [`try...catch` block](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) to handle errors. For example: ```js async function myFunction() { try { const myPromiseValue = await myAsyncFunction(); console.log(myPromiseValue); } catch (error) { console.log(error); } } ``` ## `then()` With the `then()` syntax, you can instruct your code to handle promises by running a function once the promise is resolved. For example: ```js asyncFunc().then((resolvedPromiseValue) => { // Do something with resolvedPromiseValue. console.log(resolvedPromiseValue); }); ``` ### Handle `then()` return value If you return a value in the function passed to `then()`, that value is always a promise. For example: ```js const returnValue = asyncFunc().then((resolvedPromiseValue) => { return "The value of the resolved promise is: " + resolvedPromiseValue; }); // returnValue is a promise. ``` You can handle this promise in the same way as any other promise: - Use `async`/`await` to use the value synchronously in your code. For example: ```js async function myFunction() { const secondPromiseValue = await firstAsyncFunc().then( (firstPromiseValue) => { return secondAsyncFunc(firstPromiseValue); } ); console.log(secondPromiseValue); } ``` - Chain `then()` methods. For example: ```js firstAsyncFunc() .then((firstPromiseValue) => { return secondAsyncFunc(firstPromiseValue); }) .then((secondPromiseValue) => { console.log(secondPromiseValue); }); ``` Both of the above examples log the same value to the console. ### Handle errors with `then()` If your promise is rejected, then something has gone wrong in the asynchronous code and you need to handle the error. Use the [`catch()` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) to handle errors: ```js myAsyncFunction() .then((myPromiseValue) => { console.log(myPromiseValue); }) .catch((error) => { console.log(error); }); ``` ## Handle multiple promises simultaneously Waiting for multiple promises to be fulfilled before the execution of some code is supported. You can run multiple asynchronous functions and wait for their promises simultaneously using the [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) method. It accepts multiple promises in an array, and returns an array of resolved values. If any of the promises are rejected, the method throws an error. For example: ```js Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); // Expected output: [ // promise1ResolvedValue, // promise2ResolvedValue, // promise3ResolvedValue // ] }); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Code Placement ## Article: About Code Placement ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md ## Article Content: # About Code Placement When developing websites, it's important to understand where to place your code so it runs in the right context, whether that's in the browser, on the server, or across all pages. This article gives an overview of the different types of code files available, and explains when and how to use each one. ## Supported IDEs You can store your code using the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor). ## Frontend code Frontend code runs in the site visitor's browser and here you define the interactive parts of your site. This includes things like: - Responding to user input - Updating the UI - Making calls to backend services Since this code is exposed to the public, avoid including sensitive information, such as API keys or private logic. Use frontend code for: - Customizing how a page looks or behaves - Listening for events like button clicks or form submissions - Calling functions defined in backend [web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) (`.web.js`) ### Running code on all pages with global code Global code is code that runs on every page of your site. This is logic that's shared across all pages, such as managing a universal header or footer, or controlling global UI elements like a site-wide search bar or shopping cart icon. To add global code, edit the `masterPage.js` file. You'll find it in: - Wix Studio: The **Page Code** section of the **Code** panel - Wix Editor: The **Page Code** section of the the **Code** sidebar - Local IDE: The `src/pages` folder when using [Git integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md)
**Important:** `masterPage.js` has its own `onReady` event handler. Be careful not to duplicate logic from individual page `onReady` handlers, as both run in parallel.
#### Retaining an element's state You can also use global code to retain an element's state across pages. For example, if you change the source of an image element that appears on all pages, that change won't persist when navigating to another page, or even when returning to the original page. To make changes persistent across pages, use the [Storage API](https://dev.wix.com/docs/sdk/frontend-modules/storage/introduction.md): 1. In the relevant event handler, call [`setItem( )`](https://dev.wix.com/docs/sdk/frontend-modules/storage/storage/set-item.md) to store a value that reflects the element's new state. 1. In the `masterPage.js` `onReady` function, read the stored value and update the element accordingly. > **Note:** Avoid importing functions from `masterPage.js` into individual page files. Doing this causes the `onReady` function in `masterPage.js` to run twice on those pages. For shared logic, create code on a separate public file and import it where needed. ### Running code on a specific page Each page on your site has its own code file. Use these files to add functionality that's specific to that page, for example: - Handling user input - Customizing UI behavior - Calling backend functions. #### Public files If you find yourself repeating the same code across multiple page files, it's better to move shared logic to a separate **public** file. That way, you can write it once and import it wherever it's needed: Whether in a page file, a backend file, or another public file. To share a function across multiple files: 1. Create a public file and export the function from it. 1. Import the function into any page where it's needed. You can find your public files in: - Wix Studio: The **Backend & Public** section of the **Code** panel - Wix Editor: The **Backend & Public** section of the the **Code** sidebar - Local IDE: The `src/public` folder when using [Git integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) Use ES module syntax (`import`/`export`) to access functions between files. Always include the function name in curly braces when importing a named export: ```js import { myFunction } from 'public/bookings.js' ``` Leaving out the curly braces attempts to import a default export: ```js import myFunction from 'public/bookings.js' ``` To access all functions from a module, import the entire module without curly braces: ```js import * as wixSiteStores from '@wix/site-stores'; ```
**Tip:** Your page code, which is also publicly accessible, doesn't appear in the list of public files. To see your page code, go to the **Page Code** section and click on the page in the menu.
### Responding to element actions with event handlers Page elements like buttons, text, and input fields can trigger events when site visitors interact with them. The functions that handle these events are called event handlers. There are following types of event handlers: - Dynamic event handlers. Learn how to add an event handler in [Add an Event Handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/add-an-event-handler.md). - Static event handlers (deprecated). Learn more about static event handlers in [About Static Event Handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-static-event-handlers.md). ### HTML Adding raw HTML directly in the code editor isn't supported. That said, there are several ways to include or work with HTML on your site: - Add marketing code using [marketing integrations](https://support.wix.com/en/marketing-tools/marketing-integrations-tracking). - Set the [HTML property](https://www.wix.com/velo/reference/$w/text/html) of a text element to render HTML content. - Display external HTML content by [embedding a site element](https://support.wix.com/en/article/wix-editor-using-iframes-to-display-visible-content-on-your-site). - Insert [custom code snippets](https://support.wix.com/en/article/embedding-custom-code-to-your-site) into the head or body of your site pages. - Use [custom elements](https://support.wix.com/en/article/wix-editor-adding-a-custom-element-to-your-site) to define and embed your own HTML-based components. ## Backend code Backend code runs on Wix's secure servers and isn't exposed to site visitors. This is the place for logic that needs to be protected, like: - Interacting with sensitive data - Sending emails - Handling payments - Verifying permissions Use backend code for: - Performing secure database operations - Responding to frontend requests by using web modules - Handling server-side events and scheduled tasks - Exposing custom HTTP methods to other services ### Backend files Backend files are where you define server-side logic. They include code that runs securely on the web server rather than in the browser. This is where you define functions from backend libraries such as `@wix/pay`, `@wix/members`, or `@wix/events`. You can find your backend files in: - Wix Studio: The **Backend & Public** section of the **Code** panel - Wix Editor: The **Backend & Public** section of the the **Code** sidebar - Local IDE: The `src/backend` folder when using [Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) There are 2 main file types used in the backend: - **.web.js:** Web modules that can be called from the frontend. You can configure permissions for each function to control who can access them. Learn more about the .web.js files in [About Web Modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md). - **.js:** Internal backend files that can't be accessed directly from the frontend. Use these for secure server-side logic. > **Note:** You may also see `.jsw` files in older projects. These have been deprecated in favor of `.web.js` files but are still supported. Learn more about `.jsw` files in [Call Backend Code from a jsw Web Module](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/call-backend-code-from-the-frontend.md). #### Accessing functions from a .js file To access functions from a `.js` file in the frontend, import the function into a `.web.js` file first, then import that `.web.js` function into your frontend code. Import the `.js` file into the `.web.js` file: ```js import { webMethod, Permissions } from '@wix/web-methods'; import { deleteStaffMember } from 'backend/bookings' export const deleteStaff = webMethod( Permissions.Anyone, async (staffMemberid) => { return await deleteStaffMember(staffMemberid); } ); ``` Import the `.web.js` file in your page file: ```js import { deleteStaff } from 'backend/staff.web' ``` If you try to import a `.js` file directly into a frontend file, you'll see this error: ``` Access to backend script 'backend/calculations.js' denied! Client-side scripts can only import web-modules from backend code context. ``` ### Backend events Many Wix modules provide built-in events that trigger when specific actions occur. For example, uploading a file to the Media Manager triggers the `onFileUploaded` event, and successfully paying an invoice triggers the `onInvoicePaid` event. These events let you run custom logic in response to key moments in your site's workflow. To run an event, you need to add an `events.js` file. Learn more about adding backend events in [About Backend Events](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md). ### Handling HTTP requests To expose your site's functionality to the outside world, like enabling `GET`, `PUT`, `POST`, or `DELETE` requests from external services, you can define custom HTTP functions. To create an HTTP function, you need to add an `http-functions.js` file. Learn more about the HTTP functions in [About Custom Site APIs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md). ### Using data hooks when data collection changes Data hooks let you run code before or after changes are made to your site's data collections. They're useful for validating, modifying, or responding to data operations like insert, update, or remove. To create data hooks, add a `data.js` file to your backend code. You can then define functions that correspond to specific data operations on your collections. Learn more [About Data Hooks](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md). ### Routers By default, Wix uses built-in routers to handle dynamic page URLs and SEO. If you need more control over how incoming requests are handled, you can create your own custom router to define the logic, routing, and data for each request. The code for both routers and data binding router hooks is defined in the `routers.js` file. Learn more about routers in [About Routers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md). ## Secrets Manager For sensitive information like API keys, OAuth tokens, or configuration settings, use the [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) instead of hardcoding values into your code. Secrets are stored securely in your [site's dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2F/developer-tools/secrets-manager) and can be retrieved in code using the [Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md). Storing secrets in the Secrets Manager improves your site's security, especially when you're collaborating with others or using version control. Each secret has a unique name, and you can access it safely in backend code without exposing it to site visitors. ## See also - [About Event Handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) - [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Development Workspaces ## Article: About Development Workspaces ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/about-development-workspaces.md ## Article Content: # About Development Workspaces The Wix development workspaces provide integrated tools for managing site architecture, codebase, and resources across Wix Studio and the Wix Editor. These workspaces enable you to organize, write, and manage a site's code and dependencies through a unified development experience. ## Core development capabilities The development environment enables you to perform the following tasks: - Organize and manage site page code and structure. - Search your codebase. - Integrate with GitHub for local development. - Manage dependencies and package installations. - Configure and manage data collections. - Access debugging and monitoring tools. ## Page code management Manage code organization for different page types and their associated functionality: - **Main pages**: Standard site pages with custom code. - **[Global page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md#running-code-on-all-pages-with-global-code)**: Site-wide code that runs on every page of a site. - **[CSS](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md)**: Global styling and design system management. - **App-specific pages**: Custom pages for installed apps that extend a site's functionality. - **[Dashboard pages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md)**: Custom pages for a site's dashboard for admin and collaborators. - **[Dynamic pages](https://support.wix.com/en/article/cms-about-dynamic-pages)**: Content-driven pages linked with a site's [CMS](#data-management). - **[Router pages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md)**: Custom routing and navigation logic. - **[Popups](https://support.wix.com/en/article/studio-editor-using-popups)**: Site modals and popups. ## Code organization Manage your codebase with the following categories for security and organization: - **Page Code**: Code that runs on individual pages to respond to user actions, customize page behavior, and integrate with public and backend functions. - **[Public](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md#public-files)**: Shared functions and logic that can be imported and used across multiple page files. - **Backend**: Backend code that runs securely on Wix's servers. - **[Service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md)**: APIs used to inject custom logic, 3rd-party integrations, and new workflows into Wix. ## Data management Manage the following types of data collections: - **[Your collections](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md)**: Custom data collections that you create using the CMS with configurable fields, data types, and permissions. - **[Wix app collections](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collections.md#app-collections)**: Data collections of installed Wix apps. - **External collections**: Data from external databases and services that you integrate with your Wix site. ## Dependency management Handle the following external dependencies and custom extensions: - **[npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md)**: Public registry of reusable JavaScript libraries. - **[Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md)**: Wix code libraries written using Velo. - **[Custom apps](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/custom-apps/about-custom-apps.md)**: Wix Apps that you create or install to extend a site's capabilities with specialized features. ## GitHub integration Connect your development environment to GitHub for version control, collaboration, and local development workflows. For more information, see [integrating a site with GitHub](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). ## Development tools Access the following development and debugging capabilities: - **Build status**: Monitor code compilation and deployment processes in real-time. - **Monitoring**: Monitor backend requests, data requests, and a site's collection storage. - **[Logging tools](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/about-wix-logs.md)**: View, filter, and search through logs using Wix logs, Google Cloud logs, or an external logging tool. - **[Secrets manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md)**: Securely store and manage sensitive information like API keys, passwords, and configuration data. - **[Automations](https://support.wix.com/en/article/wix-automations-about-the-new-automations-builder)**: Manage custom workflows that trigger actions based on specific events or conditions. ## Test and troubleshoot Run tests and troubleshoot your code: - **[Debugging](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/debug-your-code/about-debugging-your-code.md)**: Debug using the Wix Developer Console. - **[Functional testing](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/about-functional-testing.md)**: Individual testing of backend methods to verify functionality and debug. - **[Test sites](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md)**: Isolated testing site where you can safely test changes and new features before deploying to a live site. ## AI code assistants Access AI-powered code assistance for faster development and improved code quality. For more information, see [About AI Assistants](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ai-assistants/about-ai-assistants.md). ## See also - [About Code Placement](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md) - [Development Environments](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/development-environments.md) - [Site Development Features](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/site-development-features.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Manage Sites, Pages, and Elements ## Article: Manage Sites, Pages, and Elements ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/manage-sites-pages-and-elements.md ## Article Content: # Manage Sites, Pages, and Elements When working on a Wix site with code, it's important to understand how duplication and deletion affect a site's functionality. Whether you're copying an entire site, a page, or an individual element, or deleting elements with connected code, the outcome depends on how you structure and wire your code. ## Duplicating pages and sites You can duplicate [sites](https://support.wix.com/en/article/duplicating-your-site-1472847) and [pages](https://support.wix.com/en/article/wix-editor-managing-your-sites-pages), as well as [clone](https://support.wix.com/en/article/wix-studio-managing-sites-in-a-workspace) and [transfer](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) your Wix sites. Whether you're duplicating a single page, an entire site, or cloning or transferring a site, the new version maintains the code and events. ### Secrets If a site uses the [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) to store sensitive information like API keys or tokens, be aware that secrets behave differently during site operations: - **Site transfer**: The system transfers secrets to the new owner along with the site. - **Site duplication**: Secrets aren't transferred to the duplicate site. You need to manually recreate any required secrets in the duplicate site's Secrets Manager. ## Duplicating elements When you copy and paste an element that has associated code, the system duplicates the element itself, but doesn't include any events, wiring, or code linked to it.
**Important:** If you're building a Wix site that others can edit, make sure they understand the implications of changing elements that have associated code. For more information, see [Working with Collaborators](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/about-site-collaboration.md).
## Deleting elements When you delete elements from a site, the behavior of any associated code depends on the type of event handler used. For both static and dynamic frontend event handlers, the code itself isn't automatically removed, but how it's connected to the element changes. > **Note:** In the editor, if a collaborator tries to delete elements referenced by code, a confirmation prompt displays. The prompt helps prevent accidental deletions that can impact your code. The prompt displays for: > > - Elements directly referenced by code. A ![Code indicator](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b2e602834297db1e0836bd83e1de9f8a.png) icon displays for these elements as an indicator. > - Parent elements whose nested elements code references. ### Deleting an element with dynamic event handler When you delete an element with a [dynamic event handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md), the associated code isn't removed. If you add a new element and give it the same ID as the deleted one, the existing code continues to work as expected. ### Deleting an element with static event handler When you delete an element with [static event handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-static-event-handlers.md), the associated code remains, but the system removes the event wiring. Because you can no longer wire static events, this means that the event handler no longer works. If you want to continue to use the event handler code, you need to change the code to use a dynamic event handler registered to an existing element. ## See also - [About Code Placement](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md) - [Working with Collaborators](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/about-site-collaboration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Collaboration ## Article: About Site Collaboration ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/about-site-collaboration.md ## Article Content: # About Site Collaboration [Collaborators](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) let you develop a website together with other people. You can invite teammates to collaborate on site content, write and edit code, or manage business settings. You can also transfer a site to a new owner but still continue collaborating on updates and maintenance. For example, you might: - Hire a developer to build custom functionality on your site. - Work with a designer to adjust layouts and styles. - Transfer a completed site to a client and keep editing features as needed. - Share editing and testing responsibilities with a team. ## Roles and permissions To edit a site's code and functionality, collaborators must be assigned a role with **Edit Site** permissions. You do not need to give collaborators **Publish** permissions to allow code editing, but note that without it, they cannot publish changes or test features on the live site.
Tip: If a collaborator adds functionality that requires live testing, they won’t be able to test without Publish permissions.
If you should be able to work on a site after transferring it to a new owner, ask the new owner to invite you back as a collaborator with **Edit Site** permissions (see [Inviting people to collaborate on your site](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site)) and accept the invitation. ## See also - [Collaborating on a site](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) - [Inviting people to collaborate on your site](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) - [Information for site collaborators](https://support.wix.com/en/article/roles-permissions-information-for-site-collaborators) - [Transferring a premium site to another Wix account](https://support.wix.com/en/article/transferring-a-premium-site-to-another-wix-account) - [Transferring a free site to another Wix account](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) - [Considerations when working as a collaborator](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/considerations-for-collaboration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Considerations for Collaboration ## Article: Considerations for Collaboration ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/considerations-for-collaboration.md ## Article Content: # Considerations for Collaboration If you develop a site with code for someone else that you can both edit, you need to understand the limitations of what you or the other user can do with the site and its elements. Keep in mind, for example, the other user might be a designer with no coding experience. ## Site-related considerations When you duplicate a page or site, or when you clone or transfer a site, the code and events are copied to the new version but remain unchanged in the original. ## Element-related considerations - When you copy and paste an element that's connected to a dataset, the new element maintains the connection settings from the element it was copied from. - When you move an element from one page to another, the element won't work as expected since the associated code is on the original page and isn't moved to the new page. - If an element is connected to a dataset, and both are set to show on only one page, setting the element to [Show on All Pages](https://support.wix.com/en/article/showing-an-element-on-all-pages-6533570), breaks the connection to the dataset. - In the editor, if a collaborator tries to delete elements referenced by code, a confirmation prompt displays. The prompt helps prevent accidental deletions that can impact your code. The prompt displays for: - Elements directly referenced by code. A ![code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b2e602834297db1e0836bd83e1de9f8a.png) icon displays for these elements as an indicator. - Parent elements whose nested elements are referenced by code. - When you copy and paste an element that has (deprecated) event handlers associated with it, the new element won't work as expected since the event wiring and code isn't copied. ## See also - [Collaborating on a site](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) - [Inviting people to collaborate on your site](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) - [About site collaboration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/collaboration/about-site-collaboration.md) - [Transferring a premium site to another Wix account](https://support.wix.com/en/article/transferring-a-premium-site-to-another-wix-account) - [Transferring a free site to another Wix account](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: IDEs Overview ## Article: IDEs Overview ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/ides-overview.md ## Article Content: # IDEs Overview Wix provides multiple development environment options for writing code, each designed to fit different workflows and project needs. Choose the environment that best matches your development style and project requirements. ## Code editor The built-in [code editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) is available directly in Wix Studio and the Wix Editor. It provides syntax highlighting, autocompletion, error detection, and integrated debugging tools, enabling you to write and manage code without leaving the Wix platform. ## Git integration [Git Integration & CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) allows you to develop in your preferred local IDE with full version control through GitHub. This option provides the most flexibility for professional development workflows, team collaboration, and automated deployments. ## Wix IDE The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) is an advanced, browser-based development environment built on Visual Studio Code. It provides a full-featured coding experience with hot reloading, concurrent editing for multiple developers, and AI-powered assistance through the Wix AI Assistant. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Code Editor ## Article: About the Code Editor ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md ## Article Content: # About the Code Editor The code editor is Wix's built-in development environment that allows you to write, edit, and manage code directly in Wix Studio and the Wix Editor. It provides essential tools for site development without requiring external software. ## Key features - Syntax highlighting - Code autocompletion and IntelliSense support - Error detection and real-time syntax checking - File management - Integrated debugging tools and console output - JSDoc support ## See also - [About Development Workspaces](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/editors/about-development-workspaces.md) - [About the Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) - [About Git Integration with Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Properties & Events Panel ## Article: About the Properties & Events Panel ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md ## Article Content: # About the Properties & Events Panel The Properties & Events panel allows you to add properties and functionality to your page elements. With the panel you can: - Access the [API reference](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) for the selected element. - View and manually update the ID of the selected element. - Set default values for the properties of the selected element. - Add an event handler to your code to interact with the selected element.
The Properties & Events panel changes content when you select an element. The properties and event handlers listed in the panel differ based on the element that you select. ## Supported IDEs You can access the Properties & Events panel in the editor (Wix Studio and Wix Editor). It's located to the right of the code editor. ::::tabs :::Wix-Studio ![Properties & Events panel in Wix Studio](https://wixmp-833713b177cebf373f611808.wixmp.com/images/48d5758eae44d6ee60f50f963f8e2a9f.png) ::: :::Wix-Editor ![Properties & Events panel in Wix Editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e2bb59634d954873c36925900971f8eb.png) ::: :::: ## The ID property All elements come with a default ID value. When you select an element, its ID is displayed in the panel. You can manually change the ID of your element. Assigning meaningful IDs to your elements can make your code easier to maintain.

Important:
If you change the ID of an element, make sure to update any existing code that uses the ID of that element.

## Default state properties Certain boolean properties for your selected element appear under the Default Values section of the panel. They allow you to set an element's state when the page loads. This limits the need to set an element's initial state with code, keeping your code cleaner and more concise. Set the element's initial state by selecting or clearing the checkbox to the left of the property. The available default state properties are: - **Hidden:** Hides the element from your site visitors when your page loads. You can use code to unhide the element upon an action or after a set amount of time. Hidden elements take up space on your page and appear faded in the editor until they're selected. An element's hidden state can also depend on whether its parent element is hidden or collapsed. - **Collapsed:** Collapses the element and hides it from your site visitors when your page loads. You can use code to expand the element upon an action or after a set amount of time. Collapsed elements don't take up space on your page and appear with diagonal hatching lines in the editor until they're selected. Collapsing and expanding can cause other elements to shift on your page. An element's collapse state can also depend on whether its parent element is hidden or collapsed. - **Enabled:** Enables interactive elements such as [buttons](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) and [text inputs](https://dev.wix.com/docs/velo/velo-only-apis/$w/text-input/introduction.md). Elements with this property are initially in an enabled state. Clear the checkbox to disable the element when the page loads. You can use code to later enable the element when you want visitors to interact with it. Disabled elements appear faded in the editor. >**Notes:** > - The properties displayed in the panel change depending on the selected element. > - To get or set properties with code, see the [Velo API reference](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) for a full list of an element's properties. ## Event handlers Event handlers appear under the **Event Handlers** section of the Properties & Events panel. Event handlers allow you to add code that defines what happens when site visitors interact with this element. Clicking on an event handler in the panel adds the event handler's function declaration to your page code for you. ```js $w('#myButton').onClick((event) => { // Add code here }) ``` Like properties, event handlers are specific to the element. Common event handlers that appear in the panel for many elements include: - `onBlur()` - `onClick()` - `onDblClick()` - `onFocus()` - `onMouseIn()` - `onMouseOut()` - `onViewportEnter()` - `onViewportLeave()` Other event handlers can appear in the panel depending on the element that's selected. For example, when a [Gallery element](https://dev.wix.com/docs/velo/velo-only-apis/$w/gallery/introduction.md) is selected, the panel includes additional event handlers to some of the ones listed above, such as: - `onCurrentItemChanged()` - `onItemClicked()` - `onPlay()` - `onPause()` See the [Velo API reference](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) for descriptions of all the event handlers for each element. ## Events AI assistant in Wix Studio The Properties & Events panel in Wix Studio also comes with an AI Assistant button. ![AI Assistant button in Properties & Events panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/246ca4e3721894c89e2d67399846e850.png) Use the AI assistant to help create interactive code for your event handlers. The AI assistant allows you to select an event handler and then describe what the code should do. It generates a code example that you can add to your page code. ![AI Assistant interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7f633dd6c2099d996fe2ee5bb8fa6f26.png) ![AI Assistant generated code example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e88f2c787c0b892c03ac8cb601a162f6.png) It's important to note that AI can make mistakes and that you should double check that any AI generated code is accurate before using it. >**Note:** > The AI Assistant button is only supported in Wix Studio. ## Using the JavaScript SDK with event handlers Although event handlers are created using Velo APIs, you can use the JavaScript SDK in the body of a event handler. For example, you can connect a button click to a data query from a collection: ```js import {items} from "@wix/data"; $w.onReady(function () { $w('#myButton').onClick(async (event) => { // Use SDK code inside the Velo event handler const results = await items.query("MyCollection").find(); console.log('Found items:', results.items); }); }); ``` See [Develop Websites with the SDK](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/develop-with-the-sdk/develop-websites-with-the-sdk.md) for guidance on how to use the JavaScript SDK alongside Velo in your Wix projects. ## See also - [About Event Handlers in Wix](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) - [Add an Event Handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/add-an-event-handler.md) - [Velo API reference](https://dev.wix.com/docs/velo.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About JSDoc Tags ## Article: About JSDoc Tags ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-jsdoc-tags.md ## Article Content: # About JSDoc Tags [JSDoc](https://jsdoc.app/) is a popular markup language used to document JavaScript code. You can use JSDoc in a Wix environment in the same way as you would in any JavaScript environment. Adding JSDoc to your custom code allows you to benefit from [type checking and code autocompletion](#type-checking-and-autocompletion). ## Supported IDEs You can add and benefit from JSDoc using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor). - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio). - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor). You can only [import JSDoc](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/import-types-with-jsdoc.md) in the editor. ## JSDoc tags and definitions Add JSDoc tags to JavaScript files as comments. Each line of JSDoc defines an item using the following format: ```js /** * @ {} */ ```
JSDoc tags | Variable | Description | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `jsdoc-tag` | [JSDoc tag](https://jsdoc.app/) that defines what the item is.
For example, use `@typedef` to define a new type, `@property` to define the property of an object, and `@param` to define a parameter. | | `type` | Data type of the item. For example, `object`. | | `item-name` | Name or identifier for the item being documented. | | `description` | Description of the item.
Optional. |
### Where to add JSDoc in your code You can add JSDoc to your code as you would to any JavaScript code, by including the annotations just above the code you are documenting. However, when defining a method parameter type using the `@param` tag, place the JSDoc definition directly above the method declaration. #### JSDoc example In the following example, we define an `employee` type that's then applied to a method parameter. ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {number} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. * * @param {employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${ employee.hours * employee.seniority * 100 }`; } ``` ## Type checks and autocompletion When you apply JSDoc types to parameters, the editor can perform type checking and provide autocomplete suggestions for method arguments. ![Type checking with JSDoc](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ba3ab0ee4de81306214582fd0b19be3a.png) ![Autocomplete with JSDoc](https://wixmp-833713b177cebf373f611808.wixmp.com/images/db33be3b82f536a20db5803e5a77ca58.png) ## File types You can add JSDoc in any JavaScript file, meaning the file's suffix is `.js`. This includes JavaScript files in [custom apps](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-apps.md), allowing anyone using the custom app to use type checking and autocomplete. You can import types defined by Wix in some specific file types. For example, you can import backend event objects to [`events.js` files](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md).
Important: Web.md modules files with the suffix `.jsw` support autocomplete, but not type checking.
## See also - [JSDoc documentation](https://jsdoc.app/) - [Import Types with JSDoc](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/import-types-with-jsdoc.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Import Types with JSDoc ## Article: Import Types with JSDoc ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/import-types-with-jsdoc.md ## Article Content: # Import Types with JSDoc Import [JSDoc types](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-jsdoc-tags.md) into your JavaScript code to enable type checking and autocomplete functionality. ## Step 1 | Define and export a type To use a custom type, you first need to define it. If you want to use your defined type in other files, you must export the type. You can skip this step when using types defined by Wix. To define and export a type using JSDoc: 1. Use [JSDoc tags and definitions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-jsdoc-tags.md#jsdoc-tags-and-definitions) to define a type. For example: ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {number} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. */ ``` 1. Choose how to make your type available to other files: - **Global type:** By default, your type is available in any file across the site without importing. This is convenient but can cause naming conflicts if you use the same type name in different files. - **Restricted type:** Add `export {};` below your JSDoc definition to keep the type contained within its file. This prevents naming conflicts and requires explicit importing when used elsewhere. ```js /** * @typedef {object} employee * @property {string} name Employee's name. * @property {number} seniority The number of years the employee has worked at the company. * @property {number} hours The number of hours the employee works a week. */ export {}; // Makes this type file-specific ``` ## Step 2 | Access a JSDoc type The way that you import a JSDoc type depends on where and how you expose the type - [Global types](#global-types) - [Restricted types](#restricted-types) - [Editor elements](#editor-elements) - [Backend event handlers](#backend-event-handlers) - [Router methods](#router-methods) ### Global types Use the type directly without importing it. For example: ```js /** * @param {employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${ employee.hours * employee.seniority * 100 }`; } ``` ### Restricted types Import your type using `import('').` directly in the JSDoc tag. For example: ```js /** * @param {import('backend/myJSDocFile.js).employee} employee */ export function salaryCalculator(employee) { return `Salary for ${employee.name}: ${ employee.hours * employee.seniority * 100 }`; } ``` ### Editor elements You can use [Wix Editor elements](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) directly in JSDoc types without importing them. Define the JSDoc data type using `$w. Element`. For example: ```js /** @param {$w.Text} textElement */ ``` ### Backend event handlers By default, the file for [backend event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md), `events.js`, doesn't know the type of the parameter you are passing to a method. To tell your `events.js` file the parameter's type: 1. Check the event's `EventObjectName`. You can find it in the method declaration in [API reference](https://dev.wix.com/docs/velo.md). 1. Add the following code above the method declaration: ```js /** @param {import('api-module-name').Events.} parameterName */ ```
Example: onBookingCreated(.md) To add JSDoc to wix-bookings.v2 event, onBookingCreated():
  1. Open the API reference for onBookingCreated(.md). Find the EventObjectName.
    Booking created event object
  2. Add the following JSDoc annotation above the event handler method in the events.js file:
    /** @param {import('api-module-name').Events. BookingCreated} event */

/** @param {import('api-module-name').Events. BookingCreated} event */
export function wixBookings_onBookingCreated(event) {
  // Handle your event
}
### Routers By default, the [routers.js](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) file doesn't support autocomplete or type checking for [WixRouterRequest](https://dev.wix.com/docs/velo/events-service-plugins/routers/service-plugins/wix-router/wix-router-request/introduction.md) objects passed to router methods. To support autocomplete or type-checking in a `routers.js` file, add the following JSDoc annotation above the router methods: ```js /** @param {import('wix-router').WixRouterRequest} parameterName */ ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Wix IDE ## Article: About the Wix IDE ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md ## Article Content: # About the Wix IDE
**Note**: Some features described in this article aren't yet available to all users.
Wix Studio includes a code editor for adding code directly to a site's pages. For a more advanced option, the Wix IDE provides a browser-based environment built on Visual Studio Code. The Wix IDE also features the [Wix AI Assistant](https://dev.wix.com/docs/develop-websites/articles/workspace-tools/developer-tools/ai-assistants/ai-assistant-in-the-wix-ide/about-the-wix-ai-assistant.md), an intelligent companion that offers real-time coding assistance. > **Note:** > The Wix IDE is currently unavailable for sites using [Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). ## Understanding the site file structure Wix sites have a specific file structure that Wix uses to run your code.
![IDE file tree](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b13a044d60b940ae69a2f2e73333ae1c.png)
A site's file structure includes these files and folders: - The `src` folder, that contains the following folders: - [backend](#backend-folder) - [pages](#pages-folder) - [public](#public-folder) - [styles](#styles-folder) - The `jsconfig.json` file and `.wix` folder found in the repo's root folder. > **Important:** > The `jsconfig.json` file and `.wix` folder are used to support type checking and autocomplete in the IDE. You don't need to edit these files. Changes to these files aren't synced to your site and are lost when you close the IDE. > **Note:** > Wix doesn't support adding files for web crawlers and bots such as `robots.txt`, `ads.txt`, and `security.txt` to a site. If you add these files, they're ignored by Wix. Here is an explanation of the different subfolders in the `src` folder: ### Backend folder This folder contains the backend code files for a site. Some backend code files are automatically added to this folder, for example, when you add a router to a site, a `router.js` file is added to your backend folder. If you don't see the file that you need, you can create it. The backend folder may contain the following files: - **Web Module files:** These are files that allow you to expose [web methods](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) in a site's backend that you can easily call from the frontend. These files require a `.web.js` file extension. * **data.js** A file for [adding data hooks](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md) to a site's collections. * **routers.js** A file for implementing [routing and sitemap](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) functionality for a site. * **events.js** A file for implementing a site's [backend event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md). * **http-functions.js** A file for implementing [HTTP methods](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md) that are exposed on a site. * **jobs.config** A file for [scheduling recurring jobs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/scheduled-jobs/schedule-recurring-jobs.md). Jobs consist of backend code that's run at regular intervals. * **General backend files** JavaScript code files. You can import code from these files into any other backend file on a site. These files require a `.js` file extension. Use the following syntax to import code from backend files: ```js import { myFunctionName } from "backend/myFileName"; ``` Trying to import from the relative path in a site's files doesn't work. #### Config folder If you add a [Velo Package built by Wix](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/packages/working-with-velo-packages.md) to a site, the backend folder contains a folder called `__config__`. This folder contains an editable `config.json` file for defining specific settings for your package. #### Service plugin folder If you add [service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md) to a site, the backend folder contains a folder called `__spi__`. This folder contains subfolders with the code files for each service plugin. Learn more about [implementing service plugins](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/implement-service-plugins.md).
permissions.json > **Important:** > You only need to use this file if you export backend methods from `.jsw` files. We recommend using web methods and exporting backend methods from `.web.js` files instead. Learn more about [web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md). The backend folder also contains the `permissions.json` file. This file defines [permissions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md#permissions) for the methods in your web module files. The file contains a key, `"web-methods"` which contains an object. Each key in that object corresponds to a web module file in your backend folder. Name these keys with the following syntax: `"backend/{path to file}/myFile.jsw"`. The value for each file name key is an object that contains keys named after the methods in that file. Those objects contain keys for each permission level. For example: ```json { "web-methods": { "backend/myFileName.jsw": { "myFunction": { "siteOwner" : { "invoke" : // Boolean }, "siteMember" : { "invoke" : // Boolean }, "anonymous" : { "invoke" : // Boolean } } } } } ``` Set the permissions for each method using the following values: - **Owner-only access**: - `siteOwner.invoke`: `true` - `siteMember.invoke`: `false` - `anonymous.invoke` : `false` - **Site member access**: - `siteOwner.invoke`: `true` - `siteMember.invoke`: `true` - `anonymous.invoke` : `false` - **Anyone can access**: - `anonymous.invoke`: `true` - `siteMember.invoke` : `true` - `anonymous.invoke`: `true` The `"web-methods"` object must also contain a `"*"` key. The value for this key defines the default permissions that are applied to any method whose permissions you don't set manually. Here is a sample `permissions.json` file for a site with a backend file called `helperFunctions.jsw`. The file's methods are called `calculate`, `fetchData`, and `syncWithServer`. In this case anyone can call `calculate`, site members can call `syncWithServer`, and only Wix users can call `fetchData`. ```json { "web-methods": { "*": { "*": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } } }, "backend/helperFunctions.jsw": { "calculate": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } }, "fetchData": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": false }, "anonymous": { "invoke": false } }, "syncWithServer": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": false } } } } } ```
### Pages folder This folder contains code files for each of the pages on a site as well as the masterpage.js file for global code. The code you add to these files runs when visitors open pages on your site. When you add a page to a site in the editor, a code file for that page is added to this folder. The name of the file has 2 components: the name of the page that you define when you create it, and an ID for internal use. The sections are separated by a period. For example, the code file for the home page on a site may be named `Home.c1dmp.js`. The `Home` part of the file name is the name of the page, and the `c1dmp` part is an ID that Wix uses to identify the page. When you [add a dynamic page](https://support.wix.com/en/article/content-manager-about-dynamic-pages#adding-dynamic-pages) to a site, 2 code files are added to this folder corresponding to the dynamic list and dynamic item pages. When you open a page's code file, you see the same sample code that appears in the Wix Studio code editor. When you delete a page in the editor, the page's corresponding code file is deleted as well. > **Warning:** > Don't rename code files for pages. Wix uses these file names to associate the files with the appropriate pages on your site. If you rename a file, your code is ignored and a new code file is created for the page. ### Public folder This folder contains the public code files for a site. You can import code from these files into any other file on a site. Use the following syntax to import code from public files: ```js import { myFunctionName } from "public/myFileName"; ``` Trying to import from the relative path in a site's files doesn't work. ### Styles folder This folder contains custom CSS files for a site. Add custom CSS to a site in a file called `global.css`. If you don't see this file, you can create it. The styles defined in this file are applied to all the pages on the site. Many [Wix Editor Elements](https://www.wix.com/velo/reference/$w/introduction) have specially defined class names that you can use to style them. You can also create custom class names to use in your CSS code. To learn about supported elements and their class names, see the [Velo API Reference](https://www.wix.com/velo/reference/$w/styling-elements-with-css). You can also see an element's predefined classes and add custom class names to an element in the [**CSS Classes panel**](https://support.wix.com/en/article/coding-with-the-wix-studio-sidebar#css-classes-panel). Learn more about [writing CSS code for a site](https://www.wix.com/velo/reference/$w/styling-elements-with-css). ## Use the Wix IDE To use the Wix IDE, do the following: 1. Click **Code in Wix IDE** in the top right corner of the code editor. 2. The Wix IDE opens in a new browser tab. You can now edit the site's code. Changes saved in the IDE are automatically synced to the site. The IDE's autosave feature is enabled by default. You can [disable autosave](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) in the IDE's Settings editor. **Note:** To use backend methods in your public and page code files, export methods from your backend files using [web modules](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md). 3. Test your code using a [test site](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md) or by [previewing the site](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site). You can see messages logged from your code in [Wix Logs](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-wix-logs.md) or by [connecting your site](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-wix-logs/view-logs-with-google-cloud-logs.md) to Google Cloud Logs (Cloud Logging). 4. When your code is ready, publish the site to make your changes live. ## Editor-only actions You need to do certain parts of the Wix development flow in the editor. These include: - Adding [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md), [Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md), and [custom apps](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/custom-apps/about-custom-apps.md). Once you add these packages in the editor, you can import them in your code files in the IDE. - [Adding](https://support.wix.com/en/article/wix-editor-adding-a-page-to-your-site#adding-a-new-page), changing, or deleting pages. - [Changing](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md) element IDs or properties. - [Previewing](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#previewing-your-site) a site or [creating a test site](https://support.wix.com/en/article/creating-a-test-site). - [Publishing](https://support.wix.com/en/article/wix-editor-saving-previewing-and-publishing-your-site#publishing-your-site) a site. - Creating a [service plugin](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/extend-wix-business-solutions/service-plugins/about-service-plugins.md). ## Concurrent editing support Two or more site contributors can edit a site's code at the same time in the Wix IDE. Edits made in one instance of the IDE are synced to the other instance in real time. However, you can't edit a site's code in both the Wix IDE and the Wix Studio Code panel at the same time. You also can't edit your code in the Code panel if other site contributors are editing in the Wix IDE. When you open the Wix IDE, the Code panel switches to read-only mode. The Code panel displays the following message: ``` Code panel is read-only because Wix IDE is open in another tab. ``` If you want to edit a site's code in the Code panel, all site contributors must close the Wix IDE. You can then click **Start Coding** in the Code panel. You will see the following message: ``` Other user(s) have stopped editing code. Do you want to start coding? ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Wix IDE Beta Release ## Article: About the Wix IDE Beta Release ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide-beta-release.md ## Article Content: # About the Wix IDE Beta Release The Wix IDE is currently in beta release. While this release is stable and can be safely used for building and modifying production sites, it still lacks important functionality as listed in the [known issues](#known-issues) section. As we continuously improve the Wix IDE, you can stay informed about new updates in the [release notes](#wix-ide-beta-release-notes) below. As a separate environment from the editor, the Wix IDE allows you to focus on code and instantly see your code changes in Preview mode on a separate tab. Although the tutorial upon opening the Wix IDE isn't yet fully tailored to the Wix environment, you can enjoy the IDE features such as refactoring capabilities, go to definition, command palette, split view, improved navigation, and more. Learn more about working with the [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md). You can also check out the README file in your project's root level. ![Wix IDE README file interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c0f2e69771cb77b916d7c19b1aca5018.png) ## Known issues Before getting started with the Wix IDE, it's important to note the following known issues that we're working to fix: - **Auto-complete isn't fully functional** [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense), VS Code's language support feature, doesn't currently recognize all npm packages, Velo packages, Wix Blocks apps, backend modules, and editor elements as expected. You may experience false positive error highlighting for valid code that can run successfully. - **Functional testing is unavailable** [Functional testing](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/about-functional-testing.md) isn't currently available in the Wix IDE. For functional testing, use the code editor. - **Terminal isn't fully supported** The Wix IDE doesn't require the terminal console to work. While the terminal is available for use, it only offers limited functionality. - **Built-in Git functionality isn't supported** Currently, you can't initialize a local Git repository or publish to GitHub from the Wix IDE. - **Git Integration and Wix IDE can't connect simultaneously** You can't use both the [Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) and the Wix IDE at the same time. If you connect a site to GitHub with Git Integration, the Wix IDE is unavailable until you disconnect the site from GitHub. The reverse is also true. - **Settings and extensions don't persist between sessions** In the Wix IDE you can modify settings and install extensions from the [Open VSX Registry](https://open-vsx.org/). However, settings and extensions only apply to the IDE of the site you're currently working in, and aren't retained between Wix IDE sessions for the same site. You must reset your settings and re-install your extensions at the start of each new session. Note that a session concludes after 30 minutes of inactivity. ## Wix IDE beta release notes Stay up to date on the latest changes and additions as we continue to improve the Wix IDE. Your feedback is important to us. Please help us by reporting any issues that you notice while working with the Wix IDE. | Date | Description | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | August 2, 2023 | Wix IDE beta available in Wix Studio. | | March 10, 2024 | Element IDs automatically sync with your IDE when you make changes to a site. ID changes were previously out of sync and required you to sync manually. | | March 26, 2024 | The **Display IDs** toggle is now visible in the **Layers** panel of the editor, while working in the Wix IDE. ![Layers toggle](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f80b9b5e0ae3c659803e27e3d5ffa867.png) | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Git Integration with Wix CLI ## Article: About Git Integration with Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md ## Article Content: # About Git Integration with Wix CLI Git Integration & Wix CLI is a set of tools that allows you to write, test, preview, and publish code for a Wix site using your preferred IDE. The site code is stored in a GitHub repository, giving you version control and making collaboration easy.
## Code in your preferred IDE Git Integration & Wix CLI allows you to connect a site to GitHub and develop using your preferred IDE. Wix sets up a repository for your project, which you can clone to your computer or cloud workspace and write code in as you would in any other non-Wix project. Read how to [set up Git integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md). ## Test your code with the Local Editor The [Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md) allows you to test changes made to a site in real time. The code in your IDE is synced with the Local Editor so you can test your changes before committing them to your repo. You can also change the site's design in the Local Editor and sync it with your IDE. ## Preview and publish with the Wix CLI The [Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md) is a tool that allows you to work with a site locally from the terminal. You can use it to build a preview version of the site and publish it. You can also use the CLI to install [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) to the site. ## Work with multiple collaborators at once Git Integration & Wix CLI extends Wix Studio's [concurrent editing](https://support.wix.com/en/article/studio-editor-collaborating-on-a-site) capabilities. Invite other developers as collaborators on your [site](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) and your [GitHub repo](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/inviting-collaborators-to-a-personal-repository). Multiple developers can work on a site's code at once. ## Version control With Git integration, every change to a site's code is tracked in your GitHub repository, giving you a clear history of edits and the ability to revert to previous versions when needed. This makes it easy to manage updates, resolve conflicts, and maintain a stable development workflow—even when working with multiple collaborators. ## Get started - To get started, read how to [Set Up Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Set Up Git Integration with Wix CLI ## Article: Set Up Git Integration with Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md ## Article Content: # Set Up Git Integration with Wix CLI [Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) allows you to write, test, preview, and publish code for a Wix site from your preferred IDE. This article explains how to set up Git Integration & Wix CLI by connecting a site to GitHub, cloning the site's GitHub repository to your computer, and installing the CLI. > **Note:** You can also clone the repo to a cloud IDE workspace, then install and use the CLI there. ## Step 1 | Connect a site to GitHub To write a site's code locally from your computer, first create a GitHub repo to store the site's code and connect it to the site.
**Important:** - You can't connect a site to GitHub if you have [Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) set up on the site. This feature isn't supported when using Git Integration & Wix CLI. However, you can connect a site to GitHub if you have [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) installed on the site. - Once you connect a site to GitHub be careful not to delete the site's repo or revoke the Velo app's access to this repo. These actions may cause the site's GitHub connection to stop working even if you restore the repo or reinstall the app.
1. Go to the GitHub Integration section of your editor: ::::tabs :::Wix-Studio In the Code sidebar, click **GitHub** then **Connect to GitHub**. ![Connect to GitHub](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2975acc2f04e09b584d7445e64fb1107.png) ::: :::Wix-Editor Click the **GitHub Integration** ![GitHub Integration icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2b82d51bac04c8cc7a75a04789b97475.png) icon in the Code sidebar and then **Connect to GitHub**. ::: :::: 2. Click **Continue**. ![GitHub Continue button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/651ae528670f7aff87352339776dd7ed.png) 3. Click **Sign In**. Follow the prompts to sign in to your GitHub account and authorize Velo to create a new repo for you. 4. Choose an owner and enter a name for the new repo to connect to the site. Optionally, you can add a custom description as well. Click **Create**. ![GitHub Create repository dialog](https://wixmp-833713b177cebf373f611808.wixmp.com/images/df0351c67d99b9a80306dd59a9197f3f.png) 5. Click **Install** to start installing the Velo GitHub app on your new repo. This app allows the site to communicate with GitHub. You may need to authenticate your identity in GitHub before continuing. 6. Make sure that both **Only select repositories** and your new repo are selected. Click **Approve and Install**. ![GitHub Approve and Install dialog](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2f5b703a13d58cb46441c8cec881037d.png) 7. Copy the displayed terminal commands. You need them for the next section of the setup. ![GitHub terminal commands display](https://wixmp-833713b177cebf373f611808.wixmp.com/images/229c0690b02246b6a8affc58e71c321e.png) 8. Click **Close**. Your repo name and default branch are displayed in the **GitHub Integration** panel. ![GitHub Integration panel details](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d2a2eb8831668eb1640e1d29361fe29d.png) Once you connect a site to GitHub, your editor enters [read-only mode](https://support.wix.com/en/article/velo-working-with-editor-x-and-github-integration#about-read-only-mode). You can always choose to [disconnect your site from GitHub](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/disconnect-a-site-from-git-hub.md). ## Step 2 | Set up your local environment Once the site's repo it set up, you can clone it to your computer and install the Wix CLI. The commands used in this procedure accomplish the following: - Clone the site's repo to your computer. - Install the repo's dependencies. - Install the [Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md) globally on your computer. Before getting started, make sure you have the following things installed: - [Git](https://git-scm.com/download) - [Node](https://nodejs.org/en/download/), version 20.11 or later. - [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [yarn](https://yarnpkg.com/getting-started/install) - An SSH key [added to your GitHub account](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account). To set up your local environment, do the following: 1. Open the terminal on your computer. 2. Navigate to the directory where you want to store your repo's files. 3. Run the commands you copied down in the previous section. Depending on your computer's setup, you may need administrator privileges to run the commands. To see the commands again, open the site's editor and click **Local Dev Setup**. ![Local Dev Setup button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/781d050053508b55f896e6a28aa3bbbf.png) The default commands use npm as the package manager for installations. You can also use yarn to manage packages. To do this, make the following changes: - Change `npm install` to `yarn install`. - Change `npm install -g @wix/cli` to `yarn global add @wix/cli`. After the installation, the [Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md) opens in your default web browser. 4. Open the repo in your favorite local IDE to start writing code. > **Note:** Run [`wix install `](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md) in your project directory to install Wix packages. For example: `wix install @wix/events`. ## Next steps You're ready to start writing the site's code, and [testing it with the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-your-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Git Repository File Structure ## Article: Git Repository File Structure ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/git-repository-file-structure.md ## Article Content: # Git Repository File Structure The repo's file structure matches the public, backend, and page code sections in the Code sidebar of the Wix editors. The file structure includes these important elements: - The [wix.config.json](#wixconfigjson) file found in the repo's root folder. - The **src** folder, that contains the following: - The [Backend folder](#backend-folder) - The [Pages folder](#pages-folder) - The [Public folder](#public-folder) - The [velo.dependencies.json](#velodependenciesjson) file (only exists once the first npm package is installed) Add code in either the **Pages**, **Backend**, or **Public** folders. Files or folders added to the root of the **src** folder are ignored. > **Notes:** > The following Velo features can't be added to a site when using Git Integration & Wix CLI: > > - [Custom Apps](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/custom-apps/about-custom-apps.md) > - [Velo Packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) ## Pages folder This folder contains code files for each of the pages on a site as well as the `masterpage.js` file. The code you add to these files runs when visitors open pages on the site. These files correspond to the ones found in the **Main Pages** section of the **Page Code** tab in the Code sidebar. When you add a page to a site in a Wix editor in your browser, a code file for that page gets added to the repo. The name of the file has 2 components: the name of the page that you define in the editor, and an internal ID string. The sections are separated by a period. ![Page name and ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e709462e23ad2fdbb330efaacc3c3f41.png) When you [add a dynamic page](https://support.wix.com/en/article/content-manager-about-dynamic-pages#adding-dynamic-pages) to a site 2 code files are added to the site's repo corresponding to the dynamic list and dynamic item pages. When you open a page's code file, you see the same sample code that appears in these code files in Wix editors in your browser. ![Sample code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/50c249b2e60957f6e2a3e0335ff1eed5.png) When you delete a page in a Wix editor in your browser, the page's corresponding code file is deleted from the repo. > **Notes:** > > - You can't create new code files for pages from the IDE. To add a file, create a new page for the site in a Wix editor in your browser, and [sync](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/working-with-the-local-editor.md#sync-design-changes-to-your-ide) the site with the local IDE. > - Don't rename code files for pages. Wix uses these file names to associate the files with the appropriate pages on the site. If you rename a file, the code is ignored and a new code file is created for the page. ## Backend folder This folder contains the backend code files for a site. These files correspond to the ones found in the **Backend** section of the **Public & Backend** tab in the Code sidebar. Add the following files to this folder to include them in the site: - [**Web Modules:**](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) These are files that allow you to expose functions in a site's backend that you can run in frontend code. These files require a `.web.js` file extension. > **Note:** For web modules with a `.jsw` extension, permissions are managed using the [permissions.json](#permissionsjson) file. Note that `.jsw` web modules are deprecated, although still supported for backward compatibility for both Wix Editor and Wix Studio. - **data.js:** A file for [adding data hooks](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/data-api/about-data-hooks.md) to the site's collections. - **routers.js:** A file for implementing [routing and sitemap](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md#routing-code) functionality for the site. - **events.js:** A file for implementing the site's [backend event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md). - **http-functions.js:** A file for implementing [HTTP endpoints](https://www.wix.com/velo/reference/wix-http-functions/introduction) that are exposed on the site. - **jobs.config:** A file for [scheduling recurring jobs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/scheduled-jobs/schedule-recurring-jobs.md). Jobs consist of other backend code that's run at regular intervals. - **General backend files:** JavaScript code files. You can import code from these files into any other backend file on the site. These files require a `.js` file extension. Use the following syntax to import code from backend files: ```javascript import { myFunctionName } from "backend/myFileName"; ``` Trying to import from the relative path in the site's repo doesn't work. ### permissions.json
When using `.jsw` (deprecated) web modules, you can't change permissions in the editor when using Git Integration & Wix CLI. Instead, use the permissions.json file to set function permissions.   The backend folder also contains the **permissions.json** file. This file defines [permissions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md#permissions) for the functions in web module files. The file contains a key, `"web-methods"`, whose value is an object that contains keys named after the web module files in the backend folder. Name these keys with the following syntax: `"backend/{path to file}/myFile.jsw"`. The value for each file name key is an object that contains keys named after the functions in that file. Each function key has a value with the following format: ```json "myFunction": { "siteOwner" : { "invoke" : // Boolean }, "siteMember" : { "invoke" : // Boolean }, "anonymous" : { "invoke" : // Boolean } } ``` These values reflect the different levels of web module function permissions. You should set them using the following options: - **Owner-only access**: - `siteOwner.invoke`: `true` - `siteMember.invoke`: `false` - `anonymous.invoke` : `false` - **Site member access**: - `siteOwner.invoke`: `true` - `siteMember.invoke`: `true` - `anonymous.invoke` : `false` - **Anyone can access**: - `anonymous.invoke`: `true` - `siteMember.invoke` : `true` - `anonymous.invoke`: `true` The `"web-methods"` object must also contain a `"*"` key. The value for this key defines the default permissions that are applied to any function whose permissions you don't set manually. Here is an example `permissions.json` file for a site with a backend file called `helperFunctions.jsw`. The file's functions are called `calculate`, `fetchData`, and `syncWithServer`. In this case anyone can call `calculate`, site members can call `syncWithServer`, and only site owners can call `fetchData`. ```json { "web-methods": { "*": { "*": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } } }, "backend/helperFunctions.jsw": { "calculate": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": true } }, "fetchData": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": false }, "anonymous": { "invoke": false } }, "syncWithServer": { "siteOwner": { "invoke": true }, "siteMember": { "invoke": true }, "anonymous": { "invoke": false } } } } } ```
## Public folder This folder contains the public code files for a site. These files correspond to the ones found in the **Public** section of the **Public & Backend** tab in the Code sidebar. You can import code from these files into any other file on the site. Use the following syntax to import code from public files: ```javascript import { myFunctionName } from "public/myFileName"; ``` Trying to import from the relative path in the site's repo doesn't work. ## velo.dependencies.json This file is created automatically when you install the first npm package. Wix uses this file to track the [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) installed on a site. The file is updated automatically when you [install a package](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md#wix-install) with the Wix CLI. Don't change this file manually. ## wix.config.json Wix uses this file to associate a repo's code with a particular site and [UI version](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md). This file is updated automatically when the repo is synced with the Wix editors. Don't change this file manually. ## See also - [Publish a Site with Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md) - [Test Code in the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md) - [Fix a Broken Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/fix-a-broken-git-integration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix CLI Commands ## Article: Wix CLI Commands ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md ## Article Content: # Wix CLI Commands This article documents the CLI commands for working with a Wix site. For a detailed explanation of the process and how to initially set up the CLI, see [Set up Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md). ## Command overview | Command | Description | | ------------------------------- | -------------------------------------------------------- | | [wix dev](#wix-dev) | Opens a local development environment for a Wix site. | | [wix install](#wix-install) | Installs a package to a site. | | [wix update](#wix-update) | Updates a package installed on a site. | | [wix uninstall](#wix-uninstall) | Uninstalls a package from a site. | | [wix publish](#wix-publish) | Publishes a site to production. | | [wix preview](#wix-preview) | Creates a shareable version of a site before going live. | | [wix login](#wix-login) | Logs in to a Wix account. | | [wix whoami](#wix-whoami) | Displays the username of the logged-in Wix user. | | [wix logout](#wix-logout) | Logs out of a Wix account. | ## wix -h ```bash wix -h ``` Displays a list of supported CLI commands in the terminal. You can also run `wix [command] -h` to get detailed help for a command. ## wix dev ```bash wix dev ``` Opens a site's Local Editor. ### wix dev flags | Flag | Description | | ---------- | --------------------------------------------------------------------------------------- | | `--tunnel` | Use this flag to allow the editor to connect to the CLI when running it on a cloud IDE. | ## wix install ```bash wix install [options] ``` Installs a code package to a site's repo. If the site's repo has a `yarn.lock` file, the installer uses yarn to install packages by default. Otherwise, the installer uses npm by default. To specify which package manager to use, include an `--npm` or `--yarn` options flag. Example install command: ```bash wix install --yarn marked ``` Once the package is installed, a message is displayed in the terminal. ```bash marked@4.0.16 successfully installed. ``` ### wix install flags | Flag | Description | | -------- | -------------------------------------------------------- | | `--npm` | Forces the installer to use npm as the package manager. | | `--yarn` | Forces the installer to use yarn as the package manager. | ## wix update ```bash wix update [options] ``` Updates a code package installed on a site's repository. ## wix uninstall ```bash wix uninstall [options] ``` Uninstalls a code package from a site's repository. ## wix publish ```bash wix publish ``` Publishes a site. You can choose to use either the code in the default branch of a site's repo or the local code in your IDE as the publishing source. ```bash ? What would you like to publish? › - Use arrow-keys. Return to submit. ❯ Latest commit from origin/main Local code ```
**Important:** Publishing local code leaves the live site and the site's GitHub repo out of sync. If you publish from the repo later without pushing the local code, the local code is overwritten and may be lost.
For more information, see [Publish a Site with Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md). ## wix preview ```bash wix preview ``` Builds a preview version of a site. You can only use this command if you have previously published the site. You can build the preview from the code in the default branch of a site's repo or from the local code in your IDE. ```bash ? What would you like to preview? › - Use arrow-keys. Return to submit. ❯ Latest commit from origin/main Local code ``` Once the preview site is ready, a URL to the site is displayed in the terminal. ```bash Creating preview deployment... Your preview deployment is now available at http://wix.to/JOaBagO ``` > **Note:** > > - This preview isn't a [test site](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/work-with-test-sites/about-test-sites.md) and doesn't appear in the Release Manager. > - The preview uses the live versions of any [HTTP functions](https://www.wix.com/velo/reference/wix-http-functions) exposed on the site. This means you can't use the preview to test changes to HTTP functions. To test HTTP functions, commit and push them to the site's repo and use [functional testing](https://dev.wix.com/docs/develop-websites-sdk/test-your-site/test-backend-functions/test-backend-functions-with-functional-testing.md) in the editor. ## wix login ```bash wix login ``` Logs in to a Wix account. This allows the CLI to perform actions on that account's sites. ## wix whoami ```bash wix whoami ``` Displays the username of the currently logged-in Wix user. ## wix logout ```bash wix logout ``` Logs out of a Wix account. ## See also - [About Automated Workflows and the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-automated-workflows-and-the-wix-cli.md) - [Testing your code with the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Local Editor ## Article: About the Local Editor ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md ## Article Content: # About the Local Editor > **Note:** > This article refers to 2 different versions of the editor: > > - **Regular Editor**: The editor that's generally used to build Wix sites. > - **Local Editor**: A specialized version of the Regular Editor that's used when developing a site in a local IDE. The Local Editor is a tool for testing code added to a site in a local IDE using [Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). You can use the Local Editor to test code in real time and make changes to a site's design that you can sync with the IDE. For detailed instructions on this flow, see [Test Code in the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md). ## Differences in the Local Editor Working in the Local Editor is different from working in the Regular Editor. Please note the following changes: - The **Publish** button is replaced with the **Save** button. - Code files are read-only. To make changes to a site's code, use the local IDE. - Packages & Apps are read-only. To add, update, or delete packages and apps, use the local IDE. - The Wix IDE is disabled, use the local IDE instead. - If you duplicate a page on a site, the page's code isn't included in the copy. - Changes to [collection fields](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md) are immediately reflected on the live site, even before you publish. ## Making changes to site design The Local Editor allows you to make changes to a site's design such as adding or modifying elements and pages. These changes generate a new UI version for the site which you need to sync with the IDE. Once the UI version is synced with the IDE, code files for newly added pages appear in the repo. Autocomplete is also enabled for any newly added page elements. ### About UI versions UI versions are snapshots of a site's design. Saving any design changes to a site including adding a new page or adding or modifying elements generates a new UI version. UI versions are generated in both the Local Editor and the Regular Editor. Wix uses UI versions to match the code in the IDE with a site's design. The current UI version for the code in a site's repo is indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/git-repository-file-structure.md#wixconfigjson) file. ```json { "siteId": "7a6f62c7-bc3c-4d9d-8e06-29741d71c704", "uiVersion": "4" } ``` ## See also - [Test Code in the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Test Code in the Local Editor ## Article: Test Code in the Local Editor ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md ## Article Content: # Test Code in the Local Editor > **Note** > This article refers to 2 different versions of the editor: > > - **Regular Editor**: The editor that's generally used to build Wix sites. > - **Local Editor**: A specialized version of the Regular Editor that's used when developing a site in a local IDE. The [Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md) is a tool for testing code added to a site in a local IDE using [Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). You can use the Local Editor to test your code in real time and make changes to the site's design that you can sync with your IDE. This article explains how to test your code in the Local Editor and sync any design changes you make. ## Step 1 | Open the Local Editor Open the Local Editor from your computer using the [Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md). 1. In a terminal, navigate to the clone of the site's GitHub repository. 2. Run: `wix dev`. A new tab with the Local Editor opens in your default browser. Notice the **Local Editor** tag in the top bar on the left. > **Note:** If you're running the CLI on a cloud provider's IDE, such as GitHub Codespaces, add the `--tunnel` flag to the `wix dev` command. This makes the full command `wix dev --tunnel`. ### Reopen a closed Local Editor The Local Editor needs both the terminal and a browser tab open to run. If you close the browser tab, your terminal displays a URL you can use to reopen the editor in your browser. You can also do this by pressing `e` in the terminal. If you close your terminal, the browser displays an error screen. In this case, you need to open the Local Editor again by running `wix dev` in your terminal. ## Step 2 | Test your code Once the Local Editor is open, you can use it to test your code in real time. 1. Edit the site's code in your IDE and save it. ```js $w.onReady(function () { console.log("Hello World!"); }); ``` Your code is automatically synced with the Local Editor. You can see it in the editor's code panel. 2. In the Local Editor, click **Preview**. The site code runs. > **Note:** Even though you can see your code changes in the Local Editor, they don't appear in the Regular Editor until you [push them to GitHub](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md#step-1--push-code-to-github). Changes don't appear on your live site until you [publish](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md#step-2--publish-the-site). ### Sync design changes to your IDE If your testing reveals the need for design changes, you can make the changes directly in the Local Editor. The Local Editor and Regular Editor are synced, so any changes made in one are immediately reflected in the other. However, they aren't reflected in the code in your IDE. To sync the changes with your IDE, do the following: 1. Click **Save**. Clicking this button creates a new UI version. 2. If you see a modal asking you to save changes in the editor to local code, click **Continue**. ## Next steps When you're happy with the way your code runs, [push it to GitHub and publish the site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Regular Editor when Connected to GitHub ## Article: About the Regular Editor When Connected to GitHub ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-regular-editor-when-connected-to-git-hub.md ## Article Content: # About the Regular Editor when Connected to GitHub > **Note:** > This article refers to 2 different versions of the editor: > > - **Regular Editor**: The editor that's generally used to build Wix sites. > - **Local Editor**: A specialized version of the Regular Editor that's used when developing a site in a local IDE. When you connect a site to GitHub, the Regular Editor undergoes some changes to facilitate code management and version control. The code in the Regular Editor is automatically synced with the [default branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch) of the connected repo. Because a connected site's code is managed outside of the editor, the code in the Regular Editor is in [read-only mode](#about-read-only-mode). The name of the connected repo as well as the details of commits to the default branch appear in the **GitHub Integration** panel of the sidebar. ![GitHub Integration panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d2a2eb8831668eb1640e1d29361fe29d.png) ## About read-only mode While a site is connected to GitHub, the Regular Editor is in read-only mode. This involves the following changes: - Code files are read-only. You can't make any changes to the site's code or add new files. Use the local IDE to [add new files and change code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/git-repository-file-structure.md). - You can add [dynamic event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) to elements using code. To see the event handlers supported for each element, check the [Velo API Reference](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md). - Changes to the [fields](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md) in a collection are immediately reflected on the live site, even before you publish. - If you duplicate a page on the site, the page's code isn't included in the copy. ## See also - [Test Code in the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md) - [Publish a Site with Git Integration and Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Automated Workflows and the Wix CLI ## Article: About Automated Workflows and the Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-automated-workflows-and-the-wix-cli.md ## Article Content: # About Automated Workflows and the Wix CLI You can use the Wix CLI in your preferred automated workflow environment by [authenticating it with an API key](https://support.wix.com/en/article/about-wix-api-keys). For example, you can create [GitHub Actions](https://docs.github.com/en/actions) workflows to streamline and automate site development tasks, such as creating preview deployments and publishing updates to a site. Learn how to [Set Up GitHub Actions to Work with the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-hub-actions.md). ## Suggested workflows Setting up GitHub Actions workflows to work with the Wix CLI allows you to streamline and automate site development procedures. Some suggested workflows include: 1. Create a preview deployment: Build a live, shareable preview version of a site whenever a new pull request is opened, based the updated code in the pull request's branch. 2. Publish when pushing changes: Automatically publish a site whenever changes are pushed to the default branch of the site's repo. ## See also - [Set Up GitHub Actions to Work with the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-github-actions-to-work-with-the-wix-cli.md) - [Learn more about GitHub Actions](https://docs.github.com/en/actions) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Set Up GitHub Actions ## Article: Set Up GitHub Actions ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-hub-actions.md ## Article Content: # Set Up GitHub Actions To enable a GitHub Actions workflow to perform actions on a Wix site, the workflow must set up and authenticate the Wix CLI using an API key. This allows you to automate tasks like creating preview deployments and publishing site updates directly from your GitHub repository. Follow the steps below to set up GitHub Actions with the Wix CLI. ## Prerequisites Before setting up GitHub Actions, ensure you have: - A Wix site [connected to GitHub](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md) - Admin access to your Wix account (required for API key generation) - Admin access to the GitHub repository > **Note:** You can also [set up Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md#step-2--set-up-your-local-environment) locally for testing. ## Step 1 | Generate an API key An API key allows GitHub Actions to authenticate with your Wix account and perform CLI operations on your sites. To generate an API key: 1. Go to the [API Keys Manager](https://manage.wix.com/account/api-keys) 2. Click **Generate API Key** 3. Select the **Wix CLI - Git Integration** permission 4. Copy and securely store the generated key > **Important:** Keep your API key secure and never commit it directly to your repository. ## Step 2 | Store the API key as a GitHub secret Visit the site's GitHub repo and [create a repository secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) to store the API key. This makes your API key available to use securely in the repo's GitHub Actions workflows. ## Step 3 | Set up the CLI in the GitHub Actions workflow Configure the GitHub Actions workflow to set up and authenticate the Wix CLI: 1. In the GitHub Actions workflow, [set up the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md#set-up-git-integration-with-wix-cli) by including the following command: ```sh npm install ``` 2. Next, authenticate it by including the following command: ```sh npm run wix login -- --api-key ${{ secrets.WIX_CLI_API_KEY }} ``` ## Step 4 | Include CLI commands The workflow can now run authenticated Wix CLI commands to interact with the site. For example: - Include `npm run wix preview` in the workflow to [build a preview version of the site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md#wix-preview) based on the current branch. You can set up a workflow to create a preview version whenever a new PR is created. - Include `npm run wix publish` in the workflow to [publish the site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md#wix-publish) based on the repo's default branch. You can set up a workflow whenever a PR is merged to the default branch. ## See also - [About Automated Workflows and the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-automated-workflows-and-the-wix-cli.md) - [Wix CLI Commands](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md) - [Generate an API Key](https://dev.wix.com/docs/go-headless/getting-started/setup/authentication/generate-an-api-key-for-admins.md) - [Using Secrets in GitHub Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Publish a Site with Git Integration and Wix CLI ## Article: Publish a Site with Git Integration and Wix CLI ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md ## Article Content: # Publish a Site with Git Integration & Wix CLI Once you've used [Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) to edit a site's code and test it, you're ready to move it to the live site. This process includes 2 stages: 1. Pushing the code to the site's GitHub repository. 2. Publishing the site. ## Step 1 | Push code to GitHub A site's editor is synced with the [default branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches#about-the-default-branch) of the site's GitHub repository. Usually, this is the `main` branch, but you can [change it](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/changing-the-default-branch) to any branch you want. To sync the code with the editor, push a commit to the repo's default branch. You can use other GitHub features like branches, pull requests, and reviews as you normally would. Once you push a commit to the default branch, the code appears in the editor. The commit details appear in the **GitHub Integration** panel of the Code sidebar (Wix Editor), or in the **Github** section of the Code sidebar (Wix Studio). ![GitHub Integration panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d2a2eb8831668eb1640e1d29361fe29d.png) > **Note:** > There may be a delay between when you push a commit to GitHub and when it appears in the editor. ## Step 2 | Publish the site When working with Git Integration & Wix CLI, you can publish a site either from the editor or from the [Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md). The different publishing options use different copies of the site's code and different [UI versions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-the-local-editor.md#about-ui-versions), as follows: | Publishing Method | Code Version | UI Version | | ------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Editor | The code in the default branch of the site's repo. | The latest UI version, even if this version doesn't align with the code in the site's repo. | | CLI - Latest commit | The code in the default branch of the site's repo. | The UI version indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/git-repository-file-structure.md#wixconfigjson) file in the site's repo. | | CLI - Local code | The code saved in your IDE even if you haven't pushed it to GitHub. | The UI version indicated in the [wix.config.json](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/git-repository-file-structure.md#wixconfigjson) file in the site's repo. | > **Note:** Before you publish your site, make sure to save any changes you've made in the editor. Unsaved changes won't be included in the published site. ### Publish from the editor To publish a site from the editor do the following: 1. Open the editor. 2. Click **Publish** on the top-right. 3. Note that a modal may appear warning you that you are about to publish the latest UI version. Click **Continue**. ### Publish from the CLI To publish a site from the CLI, do the following: 1. Open a terminal and navigate to the site's repo. 2. Run this command: `wix publish`. 3. When you publish from the CLI, you can either use the code in the repo's default branch, or the code saved in your IDE. - To publish from the default branch, select `Latest commit from {the repo's default branch}`. - To publish from the code on your local environment, select `Local code`. **Important:** Publishing local code leaves the live site and the site's GitHub repo out of sync. If you publish from the repo later without pushing your local code, your local code is overwritten and may be lost. 4. Once you make a selection, the source of the code and UI version that are being used to publish are displayed in the terminal. A link to a preview site is also displayed. If you're sure you want to publish, press `y`. The terminal displays the URL of the published site and links to view logs and backend traffic. ## See also - [Wix CLI commands](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/wix-cli-commands.md) - [About Automated Workflows and the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-automated-workflows-and-the-wix-cli.md) - [Fix a Broken Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/fix-a-broken-git-integration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Disconnect a Site from GitHub ## Article: Disconnect a Site from GitHub ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/disconnect-a-site-from-git-hub.md ## Article Content: # Disconnect a Site from GitHub If you need to, you can disconnect a site from GitHub. When you do this, the site retains the code from the most recent commit to the GitHub repo's default branch. After disconnecting, the repo still exists in GitHub but changes to it aren't reflected on the site. Once a site is disconnected from GitHub, the editor exits read-only mode.
**Important:** Once you disconnect a site from GitHub and from a repo, you can't reconnect the site to that repo again. If you reconnect a site to GitHub later, a new repo is created.
To disconnect from GitHub: 1. Click the **GitHub Integration** icon on the sidebar. 2. Hover over the repo's name and click the **More Actions** icon. 3. Select **Disconnect from GitHub**. 4. In the modal, click **Disconnect from GitHub**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Fix a Broken Git Integration ## Article: Fix a Broken Git Integration ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/fix-a-broken-git-integration.md ## Article Content: # Fix a Broken Git Integration [Git Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) relies on the [Velo GitHub App](https://github.com/apps/velo-app) to maintain the connection between a site and its GitHub repository. If the app is uninstalled or suspended, the site's Git integration stops working. Git integration can also stop working if you make changes to the repo's settings or to your GitHub account settings. When the connection between a site and GitHub is lost, a modal indicating the problem appears in the editor. The following sections explain each of the modals you might see in the editor, and the steps you can take to fix Git integration. ## Repository not found The **GitHub repository can't be found** modal indicates that the Velo GitHub App can't find the site's repo. This happens in the following cases: - You deleted the repo. - You changed the repo's name. - You transferred the repo to another GitHub account or organization. - You changed your GitHub username. - You revoked the Velo App's access to the repo. In the first 4 cases, you need to disconnect the site from GitHub. You can do this by clicking the **Disconnect From GitHub** button in the modal. Once you've done this, [connect the site to GitHub](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/set-up-git-integration-with-wix-cli.md#step-1--connect-a-site-to-github) again and create a new repo for the site. Please note that you can't reconnect the site to the original repo. In the last case, you can restore the connection to the site's repo by granting the Velo App access to it: 1. Open [GitHub](https://www.github.com) in a new tab in your browser. 2. In the upper-right corner, click your profile photo and select **Settings**. ![GitHub profile settings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/441ce793f9e0e906e13045bae00f3da7.png) 3. On the left sidebar, under **Integrations**, click **Applications**. ![GitHub applications](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b23426c2de1f30bce9271e327a08250b.png) 4. With the **Installed GitHub Apps** tab selected, find the Velo App and click **Configure**. ![GitHub installed apps](https://wixmp-833713b177cebf373f611808.wixmp.com/images/530fb69bf394c74539ac1ef7de7f27cf.png) 5. In the **Repository access** section, click **Select repositories**. Select the site's repository from the dropdown list. Click **Save**. ![GitHub repo access and save](https://wixmp-833713b177cebf373f611808.wixmp.com/images/15850febaa9b2fdc2af55ebf1aae07d3.png) 6. Return to the editor and refresh the page. ## Velo app uninstalled The **Velo App on GitHub was uninstalled** modal indicates that you have uninstalled the Velo App from your GitHub account. To reinstall the app, take the following steps: 1. Open the [Velo App](https://github.com/apps/velo-app) page in a new tab in your browser. 2. Click **Configure**. ![GitHub Velo app](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0803fab62897ddb052397586264ccc47.png) 3. Select where you want to install the app. 4. Select **Only select repositories** and select the site's repository from the dropdown list. ![GitHub select repos](https://wixmp-833713b177cebf373f611808.wixmp.com/images/22048c4962dd3eb4c94e68a8e4e231fc.png) 5. Click **Install**. 6. Return to the editor and refresh the page. ## Velo app suspended The **Velo App on GitHub was suspended** modal indicates that you have suspended the Velo App's access to your GitHub account. To unsuspend the app, take the following steps: 1. Open [GitHub](https://www.github.com) in a new tab in your browser. 2. In the upper-right corner, click your profile photo and select **Settings**. ![GitHub settings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/441ce793f9e0e906e13045bae00f3da7.png) 3. On the left sidebar, under **Integrations**, click **Applications**. ![GitHub applications](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b23426c2de1f30bce9271e327a08250b.png) 4. With the **Installed GitHub Apps** tab selected, find the Velo App and click **Configure**. ![GitHub installed apps](https://wixmp-833713b177cebf373f611808.wixmp.com/images/530fb69bf394c74539ac1ef7de7f27cf.png) 5. In the **Danger Zone** section, click **Unsuspend**. ![GitHub danger zone](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d7fbbf528fdfbe40fda82d893f78d981.png) 6. Return to the editor and refresh the page. ## See also - [Test Code in the Local Editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/test-code-in-the-local-editor.md) - [Publish a Site with Git Integration & Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/publish-a-site-with-git-integration-and-wix-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Packages ## Article: About Packages ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/about-packages.md ## Article Content: # About Packages When writing code, you often need to build some complex feature or functionality. Sometimes you discover that someone else already wrote and implemented the functionality that you need. Instead of reinventing the wheel and writing the same code over again, you can find a pre-built package or app written by others and incorporate it into your own code. Using pre-built packages is a great way to minimize your development time and the amount of code you need to write. There are 4 types of pre-built packages and solutions available to add to a Wix site: - [SDK modules](#sdk-modules) - [Velo packages](#velo-packages) - [NPM packages](#npm-packages) - [Custom apps](#custom-apps) ## SDK modules The Wix JavaScript SDK provides a comprehensive set of modules that give you access to Wix's services and resources. These modules are specifically designed for Wix websites and work seamlessly with Wix elements and business solutions. SDK modules are npm packages with the `@wix/` namespace. For example: ```javascript import { items } from "@wix/data"; import { webMethod, Permissions } from "@wix/web-methods"; ``` Each SDK module contains detailed documentation explaining the module's functionality and implementation steps. To learn more, see the [SDK documentation](https://dev.wix.com/docs/sdk/articles/get-started/about-the-wix-java-script-sdk.md). ## Velo packages Built using the Velo APIs, Velo packages are created specifically for Wix websites and work seamlessly with Wix elements and apps. Unlike non-Wix packages, Velo package use cases are very specific to your Wix site. You can use Velo packages alongside the SDK in your development. A Velo package can include backend code files, public code files, and npm packages. A Velo package's functionality is available for use through its exported functions. An editable **config.json** file may also be included for defining specific settings for a package. You can import the functions and use them in site code just like you import and use exported functions that you write in .js and .web.js files. > **Note:** If a Velo package includes an npm package, you won't see the package installed on your site. Each Velo package contains a detailed README file explaining the package's functionality and the steps required for implementation. Some package functions are internal and can't be used directly in your site. See the package's README for the list of exposed functions available for use. To learn more, see [Working with Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md). ## npm packages The npm registry is the most popular registry of pre-built JavaScript packages. You can install public npm packages in a site directly from the npm registry. It's your responsibility to follow the npm package's developer guidelines to understand the package's functionality, in what situations it can be used, and in what situations it shouldn't be used. Once installed, you can import the package and use it in your code. To learn more, see [Working with npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md). ## Custom apps You can build custom apps using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) to share custom functionality across your sites. Once a custom app is installed on your site, the site can import custom app code, use the custom app's widgets, and the custom app's [dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) and [collections](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md) are automatically added to the site. Learn more about [custom apps](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/custom-apps/about-custom-apps.md) and how to [install custom apps on a site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About npm Packages ## Article: About npm Packages ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md ## Article Content: # About npm Packages [Node Package Manager (npm)](https://www.npmjs.com/) is a popular registry of reusable JavaScript code. In npm, each reusable library of code is referred to as a package. When developing Wix sites, you can utilize the following types of packages from npm: - **Wix packages**: Packages from the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md). Use these packages to create an interactive UI, work with data-driven content, and integrate with Wix business solutions and 3rd-party services. - **Non-Wix packages**: Packages written by 3rd-party developers. Use these packages to save time instead of redeveloping functionality that already exists. After you install an npm package, you can import it in a site's code and use it. Here are some examples of Wix and non-Wix npm packages you could use: - `@wix/data`: Manage data in your Wix CMS. - `@wix/stores`: Manage product and inventory for Wix Stores. - `@wix/site-stores`: Manage the current cart and retrieve information about product variants and availability. - `lodash`: Manipulate strings, arrays, and optimize common operations. - `@sendgrid/mail`: Easily send emails using the SendGrid service. - `axios`: Make HTTP requests from a browser or Node.js environment. ## Supported IDEs You can use npm packages with: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) You can't install or update npm packages directly from the Wix IDE, but you can use packages that were installed or updated using the editor. Follow [instructions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) for installing and updating packages in the editor, then import and use the package in the Wix IDE. ## Package support When you install a non-Wix npm package on a site, it's important that you understand the package's functionality, appropriate use cases, and limitations. Note that certain types of packages aren't supported, including [private packages](https://docs.npmjs.com/about-private-packages), packages requiring specific hardware, and packages that may pose a security risk. Here are some important considerations for using non-Wix npm packages in your code: - **Package security:** Packages are 3rd-party code and aren't verified by Wix. Therefore, installing them can put a site and users at risk. It's your responsibility to make sure that packages are safe before installing them on a site. - **Frontend vs backend code:** Make sure you use packages in the context they were designed to be used. Some packages are intended exclusively for frontend code, while others are meant for backend code. - **DOM interactions:** With Wix, you use [\$w APIs](https://dev.wix.com/docs/velo/velo-only-apis/$w/$w.md) to interact with page elements instead of directly manipulating the DOM. Therefore, some packages that include interaction with the DOM may have limited use in a site. - **React compatibility:** Some packages are designed to work with [React](https://reactjs.org/). Currently, you can only use these packages in conjunction with [custom elements](https://dev.wix.com/docs/velo/velo-only-apis/$w/custom-element/introduction.md). - **Error handling:** Errors that originate from a package's functionality are reflected in the browser console. These errors are generated by the implementation of the package itself, and not from Wix. Refer to the package's documentation for more information on resolving these errors. ## Performance considerations - Importing npm packages in frontend code increases the page's payload and may adversely affect a site's performance. ## Package limitations The non-Wix packages you install and use on a site must be compatible with Wix's runtime environment or they may not run properly. - ES modules and packages containing native modules are incompatible with Wix. - A package won't execute during runtime unless it aligns with Wix's [current Node.js version](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-java-script-support.md). - Wix has runtime limitations: terminal commands aren't supported. - For a package to be compatible with Wix, all its dependent packages must also be compatible. Otherwise, the package may not run. ## See also - [About packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/about-packages.md) - [Work with npm packages in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) - [Work with npm packages with the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md) - [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Work with npm Packages in the Editor ## Article: Work with npm Packages in the Editor ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md ## Article Content: # Work with npm Packages in the Editor Wix allows you to install [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) on a site. This article explains how to install, update, and uninstall npm packages when working in the editor (Wix Studio and Wix Editor) or Wix IDE. You can also use npm packages in your local IDE with the [Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). > **Wix IDE:** You can't install or update npm packages directly from the Wix IDE, but you can use packages that were installed or updated using the editor. Follow the instructions below for installing and updating packages in the editor, then import and use the package in the Wix IDE.
## Install an npm package Before using an npm package, you need to install it.
**Important:** By installing an npm package, you agree to the package's license agreement.
To install an npm package in the editor: 1. Navigate to **Packages & Apps** in the Code sidebar. 1. Hover over **npm** in Packages & Apps, click the plus icon ![Plus icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/174671ebdfa1dffd306ef30c4c4d4af5.png), and select **Install npm package**. The Package Manager opens. Note that the 10 most popular npm packages used on Wix sites are displayed in the Package Manager.
Package Manager interface
1. In the npm section of the Package Manager, search for the package you want to add. Instructions for how to use the package are displayed on the right side of the Package Manager. 1. Click **Install** next to the package of your choice. Once installed, you can find the package under npm in the Packages & Apps section of the Code sidebar. 1. Learn how to use the installed package from its README file. There are several ways to view the README file: - In the **Packages & Apps** section of the Code sidebar, click the npm package to view the README file in the code editor. - Hover over the package, click the **More actions** ![More actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) icon, and then select **View Documentation** to view the npm package documentation in a new browser. - Search for your package's README file at [npmjs.com](http://npmjs.com/). ## Change a package's version In the editor, you can update an npm package to its latest version, or you can change it to a previous version.
**Important:** After changing a package's version, make sure that your code is compatible with the updated version of the package.
To change a package version in the editor: 1. Navigate to **Packages & Apps** in the Code sidebar. 1. Hover over **npm** in Packages & Apps, click the plus icon ![Plus icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/174671ebdfa1dffd306ef30c4c4d4af5.png), and select **Install npm package**. 1. In the Package Manager, select **Installed Packages**. 1. Hover over the package you want to update, click the **More Actions** ![More actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) icon, and select **Choose a version**. ![Change version](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3efd4b23b92dda69d0bf42d8ae95b491.png) 1. Find the version you want and click **Install**. ![npm versions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6122f99e11216c28784295483494b29e.png) ## Uninstall a package You can uninstall npm packages that you no longer need.
**Warning:** When you uninstall a package, make sure to update any code that relies on that package.
To uninstall a package in the editor: 1. Navigate to **Packages & Apps** in the Code sidebar. 2. In the npm section, hover over the package you want to uninstall, click the **More Actions** ![More actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) icon, and select **Uninstall**. ## See also - [About packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/about-packages.md) - [About npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) - [Work with npm packages with the Wix CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Work with npm Packages with the CLI ## Article: Work with npm Packages with the CLI ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md ## Article Content: # Work with npm Packages with the CLI Wix allows you to install [npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) on a site. This article explains how to install, update, and uninstall npm packages in your local IDE with the CLI. You can also [use npm packages in the editor or the Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md). ## Install an npm package Before using an npm package, you need to install it.
**Important:** By installing an npm package, you agree to that npm module's license agreement.
To install a package in your local IDE with the CLI: 1. Open a terminal to your project's root directory. 2. Install the package of your choice by running the following command: ```bash wix install ``` 3. Open the `package.json` file in your project to see the installed package. 4. Learn how to use the installed package from its README file. You can find your package's README file at [npmjs.com](http://npmjs.com/). ## Change a package's version Using the CLI, you can update an npm package to its latest version. Currently, you can't switch to previous versions of your package using the CLI.
**Important:** After changing a package's version, make sure that your code is compatible with the updated version of the package.
To update a package to its latest version with the CLI: 1. Open a terminal to your project's root directory. 2. Update the package of your choice by running the following command: ```bash wix update ``` 3. Open the `package.json` file in your project to see the package's new version. ## Uninstall a package You can uninstall npm packages that you no longer need.
**Warning:** When you uninstall a package, make sure to update any code that relies on that package.
To uninstall a package using the CLI: 1. Open a terminal to your project's root directory. 2. Uninstall the package of your choice by running the following command: ```bash wix uninstall ``` 3. Open the `package.json` file in your project to see that the package has been removed. ## See also - [About packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/about-packages.md) - [About npm packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/about-npm-packages.md) - [Work with npm packages in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Velo Packages ## Article: About Velo Packages ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md ## Article Content: # About Velo Packages Velo packages are code libraries written using Velo code that allow you to add specific functionality to a site without spending hours writing complex code. They provide ready-made functionality that you can use as-is or extend with custom code. Although Velo packages are written using Velo APIs, you can use them alongside the Wix JavaScript SDK. ## What you can do with Velo packages - Add production-ready features maintained by Wix - Reduce boilerplate and implementation time - Use package APIs on the frontend or backend depending on the package - Combine package capabilities with SDK modules and a site's business logic ## Package content Velo packages can include read-only backend code files, public code files, and npm packages. An editable `config.json` file may also be included for defining specific settings for your package. A package's functionality is available through its exported APIs, which you can import and call in your code. > **Note**: > > - Some package APIs are internal and can't be called directly in a site. See the package's README for the list of exposed APIs available for use. > - If a Velo package includes an npm package as a dependency, it doesn't appear in the npm section of the Code sidebar in Wix Editor or Wix Studio. ## See also - Manage installation, configuration, updates, and removal: [Manage Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/manage-velo-packages.md) - Learn common usage patterns and testing guidance: [Use Velo packages in code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/use-velo-packages-in-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Manage Velo Packages ## Article: Manage Velo Packages ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/manage-velo-packages.md ## Article Content: # Manage Velo Packages [Velo Packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) are code libraries written using Velo code. Learn how to install, configure, update, and remove Velo packages from a site. ## Install a Velo package 1. Navigate to **Packages & Apps** in the Code sidebar. 1. Hover over **Velo Packages** in **Packages & Apps**, click the plus icon, and select **Install Velo package**. 1. Choose a package from the list and click **Install**. 1. Review the package's README and follow any additional setup instructions. > **Note:** By installing a Velo package that uses an npm package, you agree to that npm module's license agreement. ## Configure Velo packages Depending on the package, configuration may include: - Creating an account with a 3rd-party service. - Adding secrets in the Secrets Manager. - Adding a Wix app, such as Wix Store, to the site. - Editing the package's `config.json` file to define specific settings that affect how the package works. ## Update a Velo package version If there is an update available for a package installed on a site, you'll see a blue dot next to the package in the Code sidebar. To update a package: 1. Hover over the package name and click the more icon. 1. Select **See update**. 1. In the Package Manager, review package updates in the release notes. 1. Click **Update to X.X.X**.
**Important:** After applying a package update, make sure that the code on the site is compatible with the updated version of the package.
## Uninstall a Velo package 1. Ensure no code references remain to the package's APIs. 1. Navigate to **Packages & Apps** in the Code sidebar. 1. Hover over **Velo Packages** in **Packages & Apps**, click **Uninstall**.
**Warning:** Uninstalling a package can break code that depends on it.
## See also - [About Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) - [Use Velo packages in code](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/use-velo-packages-in-code.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Use Velo Packages in Code ## Article: Use Velo Packages in Code ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/use-velo-packages-in-code.md ## Article Content: # Use Velo Packages in Code [Velo Packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) are code libraries written using Velo code. Once installed, you can import and call APIs from Velo packages in your code. Although Velo packages are written using Velo APIs, you can use them alongside the Wix JavaScript SDK. ## Use a package Once you install a package, read the package's README. It explains setup prerequisites, which APIs are exported for use, and how to use the package. To use a package's APIs, import them in your code. Use `@velo` in import paths for Velo packages. Import paths differ depending on whether the API is exported from backend or public files. ## Import backend APIs To import backend APIs, use this syntax: ```javascript import { } from '@velo/-backend'; ``` For example, assuming you installed the `twitter-integration` package, you can call its `tweet()` API as follows: ```javascript import { tweet } from "@velo/twitter-integration-backend"; ``` > **Notes:** > > - If you want to call backend APIs from a `.js` file in page code, wrap the backend API in a [web module](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md), then import and call the web module from page code. > - Some Velo packages include web modules. You can call web module APIs directly from backend code, public code, or page code. Import them with the syntax for importing backend APIs. ## Import public APIs To import public APIs, use this syntax: ```javascript import { } from '@velo/'; ``` For example, assuming you installed the `wix-data-helpers` package, you can call its `getRandomItem()` API as follows: ```javascript import { getRandomItem } from "@velo/wix-data-helpers"; ``` ## See also - [About Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/about-velo-packages.md) - [Manage Velo packages](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/velo-packages/manage-velo-packages.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Apps ## Article: About Custom Apps ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/custom-apps/about-custom-apps.md ## Article Content: # About Custom Apps Custom apps are applications you create using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) to add reusable functionality across multiple Wix sites. Your custom apps are private and only you can install them on sites where you're a site collaborator. Once installed, all site collaborators can access the functionality of the custom app. > **Note:** Custom apps replace Velo packages built by you. Now, instead of creating packages, functionality is managed as apps. Using custom apps allows you to unify the following functionality across sites: - Widgets - Code - [Dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) - [Collections](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md) For example, you might use custom apps to do the following across multiple sites: - Ensure a standardized visual design. - Centralize and reuse custom logic and code. - Add and manage custom dashboard features. - Use uniform data structures and content setups. ## Supported IDEs You can use and access functionality from your custom apps using: - The editor (Wix Studio and Wix Editor) - The Wix IDE - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) > **Note:** You must have a Wix Studio account to create a Blocks app. ## Access custom app functionality on a site Install and manage your custom apps, including the version installed on the site, under **Custom Apps** when you click **App Market** ![App Market](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b748dd69307d0adbcf2f11e76dc779c3.png) in the Editor. Once installed, a custom app's functionality is automatically available on the site. To learn how to access the functionality, see [Install a Blocks App on a Site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md#change-the-installed-app-version). ## See also - [Install a Blocks app on a site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md#change-the-installed-app-version) - [About Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About AI Assistants ## Article: About AI Assistants ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ai-assistants/about-ai-assistants.md ## Article Content: # About AI Assistants The AI assistants are tools that use artificial intelligence to help you generate, debug, and understand code across your Wix site. Wix offers several AI assistants, each designed for specific development environments and use cases. - [AI Code assistant in the Wix Studio code panel](#ai-code-assistant-in-the-wix-studio-code-panel) - [Wix AI assistant in the Wix IDE](#wix-ai-assistant-in-the-wix-ide) - [Wix MCP](#wix-mcp) ## Supported IDEs Each of the Wix AI assistants are intended for use with a specific IDE. Use the: - [AI Code assistant](#ai-code-assistant-in-the-wix-studio-code-panel) when working in [Wix Studio](https://www.wix.com/studio). - [Wix AI assistant](#wix-ai-assistant-in-the-wix-ide) when working in the [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md). - [Wix MCP](#wix-mcp) when working in a [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md). ## AI code assistant in the Wix Studio code panel The AI code assistant in the Wix Studio code panel is an AI chatbot built into [Wix Studio](https://www.wix.com/studio). ### Use cases You can use the AI Code assistant to: - **Generate frontend and backend code:** Create custom code using both Velo APIs and the JavaScript SDK. - **Add interactive features:** Generate event handlers that respond to user interactions with site elements. - **Debug and fix errors:** Get help troubleshooting and resolving issues in your existing code. > **Note:** The assistant doesn't support requests that require interactions with 3rd-party services, external APIs, or npm packages. ## Wix AI assistant in the Wix IDE The Wix AI assistant in the Wix IDE is an AI Chatbot built into the [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md). ### Use cases You can use the Wix AI assistant to: * **Generate code:** Create frontend and backend code using Velo. * **Debug and fix errors:** Get help troubleshooting and resolving issues in your existing code. * **Search documentation:** Access information from [Wix's Velo API reference](https://dev.wix.com/docs/velo.md) and learn about new features. You can ask up to 2 follow-up questions after your initial query. After this, the assistant starts a new conversation. > **Note:** The Wix AI assistant in the Wix IDE currently supports only code using Velo APIs. JavaScript SDK-based output isn't supported yet. ## Wix MCP The [Wix MCP](https://dev.wix.com/docs/sdk/articles/use-the-wix-mcp/about-the-wix-mcp.md#required-configuration) allows you to work with Wix tools and services in your chosen AI Client. ### Use cases You can use the Wix MCP Server to: * **Search documentation:** Search information about Wix APIs. * **Generate frontend and backend code:** Create custom code using both Velo APIs and the JavaScript SDK. * **Make API calls:** Perform actions or queries for a specified account and selected site. * **Manage Sites:** Retrieve site data and perform site-level actions. ## See also * [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) * [About the Wix MCP](https://dev.wix.com/docs/sdk/articles/use-the-wix-mcp/about-the-wix-mcp.md) * [Wix MCP sample prompts](https://dev.wix.com/docs/sdk/articles/use-the-wix-mcp/mcp-sample-prompts.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Secrets Manager ## Article: About the Secrets Manager ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md ## Article Content: # About the Secrets Manager The [Secrets Manager](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/secrets-manager) allows you to securely store and manage secrets. A secret is any piece of sensitive information used in your site's code, such as API keys, OAuth tokens, and environment configuration settings. [Add secrets](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/manage-secrets-in-the-secrets-manager.md#add-a-secret) to the Secrets Manager in your dashboard by assigning each secret a [unique name](#secret-fields). You can then use that name to retrieve the secret in code using the [Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md). ![Secrets list](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fac07305a04f0a5ebfd1e1a7823a8816.png) ## Enhanced security Hardcoding secrets into your code can create security risks for your site, which can lead to data breaches and unauthorized access to your site. Here's how the Secrets Manager improves your site's security: * **Code sharing**: You may want to collaborate and share your code with others or manage it in a public repository, increasing the potential for accidentally leaking secrets. * **Code reusability**: Since code is often reused, your hardcoded secrets may be copied, increasing risk of exposure. ## Secret fields It’s important to understand the rules and limitations of a secret within the Secrets Manager. | Field name | Field description | Requirements | Required field | |-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------| | Name | ID you assign to the secret. Access it in code using the [Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md). |
  • 40 character maximum. Only letters (a-z) and numbers.
  • No spaces or special characters except hyphens (`-`) and underscores (`_`).
  • Can't begin with `wix`, regardless of case.
| Yes | | Value | Value of the secret. |
  • 3,500 character maximum.
| Yes | | Description | Information about the secret. |
  • 200 characters maximum.
| No | ## Roles and permissions Only site collaborators with the general role of **Admin (Co-Owner)** or **Website Manager** have access to the Secrets Manager. Learn more about [roles and permissions](https://support.wix.com/en/article/roles-permissions-overview). > **Important:** > - [Transferring a site](https://support.wix.com/en/article/transferring-a-free-site-to-another-wix-account) results in secrets being transferred to the new owner. > - [Duplicating a site](https://support.wix.com/en/article/duplicating-your-site-1472847?tabs=Dashboard) **doesn't** result in secrets being transferred to the duplicate site. ## See also - [Manage Secrets in the Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/manage-secrets-in-the-secrets-manager.md) in the Secrets Manager. - [About the Wix Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Manage Secrets in the Secrets Manager ## Article: Manage Secrets in the Secrets Manager ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/manage-secrets-in-the-secrets-manager.md ## Article Content: # Manage Secrets in the Secrets Manager The [Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) allows you to access and manage secrets securely, and use them in your site's code.
## Step 1 | Access the Secrets Manager There are multiple ways to access the [Secrets Manager](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/secrets-manager), depending on which interface you are using. ::::tabs :::Wix-Studio-or-Wix-Editor 1. Navigate to **Developer Tools** in the code panel. 1. Click on **Secrets Manager** in the **Security** section. ::: :::Dashboard 1. Navigate to **Developer Tools** in the dashboard menu. 1. Click on **Secrets Manager**. ::: :::: ## Step 2 | Manage your secrets Use the Secrets Manager to add, edit, and delete secrets in the Secrets Manager. ![manage secrets in ui](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6606e7105dbffe49663ea9e3b4ac49bc.png) > **Note:** Changes in the Secrets Manager are applied immediately and don't require publishing the site.
Add a secret To add a secret: 1. At the top right of the Secrets Manager, click **Add Secret**. 2. In the **Add secret** modal, fill in the [fields](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md#secret-fields). ![add panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/eaecc8c9a1c15ab26b6f4262ab89045c.jpg) 3. Click **Add Secret**. The secret is added and stored in the Secrets Manager. The name and description are displayed, and the secret value is hidden. ![added secret](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d6adc9302c5c0e6d4b013964c122c0fa.png)
Edit a secret To edit a secret:
Warning: Changing a secret's name or value breaks all code that implements the secret.
1. For the secret you want to edit, select **Edit** in the secret's **More Actions** ![More actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) menu. The **Edit secret** modal appears. ![Edit secret modal](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ed42e58ec02ea298865601cfc38f5a54.png) 2. - To change the secret's **Name** or **Description**, click the [fields](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md#secret-fields) to edit the text. - To change the secret's value, click **Retrieve value**, then click inside the secret's **Value** field that appears. 3. Click **Change Secret**.
Delete a secret To delete a secret:
Warning:
  • Deleting a secret breaks all code that implements the secret.
  • You can't restore a deleted secret.
1. For the secret you want to delete, select **Delete secret** in the secret's **More Actions** ![More actions icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) menu. The **Delete secret?** modal appears. 2. Click **Delete Secret** to confirm. The secret is removed from the Secrets Manager.
## See also - [About the Secrets Manager](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/secrets/about-the-secrets-manager.md) - [About the Wix Secrets API](https://dev.wix.com/docs/sdk/backend-modules/secrets/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About n8n Integration ## Article: About n8n Integration ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-n8n-integration.md ## Article Content: # About n8n Integration [n8n](https://n8n.io/) is a workflow automation platform that lets you connect apps and services to create automated workflows. With the Wix n8n integration, you can automate tasks involving your Wix site without writing code, connecting Wix to hundreds of other services available in the n8n ecosystem. A key capability of n8n is its built-in AI agent. By combining Wix triggers with an AI agent, you can create workflows that analyze incoming data, determine appropriate actions, and execute them automatically. Before getting started, [connect n8n to your Wix site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/connect-n8n-to-your-wix-site.md). ## What you can do with the integration The Wix n8n integration provides two types of nodes, both powered by [Wix APIs](https://dev.wix.com/docs/api-reference.md): - Wix trigger node: Starts workflows automatically when an event occurs on your site. [Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/triggers/about-triggers?apiView=SDK.md) powers the triggers and provides events across Wix business solutions, such as form submissions, new orders, booking confirmations, contact updates, and membership sign-ups. - Wix node: Performs actions on your Wix site, such as managing products and orders in your Wix Store. You can create, update, delete, and query data as part of your n8n workflows. When configuring a Wix node, you can specify parameter values manually, map them from the trigger output, or use an AI agent to generate them dynamically based on the workflow context. To see the full list of available triggers and actions, browse the options in the Wix nodes' configuration panels in n8n. Learn more about [n8n nodes](https://docs.n8n.io/workflows/components/nodes/).
**Important:** The Wix nodes are currently community nodes. Before you can use them, you must install the `@wix/n8n-nodes-wix` package. See the n8n guide on [installing community nodes](https://docs.n8n.io/integrations/community-nodes/installation/).
## AI-powered workflows The built-in AI agent in n8n is a key advantage of using n8n with Wix. Instead of building complex conditional logic, you can use an AI agent to interpret trigger data and decide what actions to take. The agent can analyze incoming information, determine the appropriate response, and generate the parameters needed for any action in your workflow, whether that's a Wix node, an email service, a CRM, or any other n8n integration. For example, you can create a workflow that recovers abandoned checkouts. When a checkout is abandoned on your site, n8n triggers the workflow and an AI agent analyzes the details and generates the parameters for a discounted recovery order and a personalized email. The workflow then creates the order and sends the email to win back the customer. To expand the capabilities of your AI agent beyond the actions available in the Wix node, you can connect the [Wix MCP server](https://dev.wix.com/docs/api-reference/articles/wix-mcp/about-the-wix-mcp.md) as a tool. Connecting the Wix MCP gives your AI agent access to additional Wix APIs. ## Authentication To connect n8n to your Wix site, you need an [API key](https://dev.wix.com/docs/api-reference/articles/authentication/about-api-keys.md) and the site ID of the site you want to connect. These are added as credentials in n8n. To learn how to set up Wix authentication in n8n, see [Connect n8n to Your Wix Site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/connect-n8n-to-your-wix-site.md). ## Use cases Here are some examples of what you can automate with the Wix n8n integration: - Abandoned checkout recovery: When a checkout is abandoned, an AI agent analyzes the details and generates parameters for a discounted recovery order and personalized email. The workflow then creates the order and sends the email. - Smart lead routing: When a form is submitted, an AI agent evaluates the submission and categorizes the lead. The workflow then routes the lead to the appropriate team member or CRM. - Automated content moderation: When a new blog comment or forum post is published, an AI agent analyzes the content and decides whether to approve, flag, or remove it. The workflow then performs the appropriate action. - Dynamic customer support: When a chat message is received, an AI agent analyzes the query, retrieves relevant order or booking information, and generates a response. The workflow then sends the response to the customer. - Cross-platform sync: When product data changes in your Wix Store, automatically update inventory in external systems like spreadsheets or software solutions like Enterprise Resource Planning (ERP). ## n8n marketplace You can search the [n8n workflows](https://n8n.io/workflows/) for workflow templates built by others that use Wix nodes. If you're a developer looking to extend the Wix n8n integration or create your own Wix-related nodes, you can build and publish them to the marketplace for other Wix users to benefit from. ## See also - [n8n Docs](https://docs.n8n.io/) - [Connect n8n to Your Wix Site](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/connect-n8n-to-your-wix-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Connect n8n to Your Wix Site ## Article: Connect n8n to Your Wix Site ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/connect-n8n-to-your-wix-site.md ## Article Content: # Connect n8n to Your Wix Site Connect your Wix site to n8n to automate workflows using Wix triggers and actions. For an overview of the Wix n8n integration and what you can do with it, see [About n8n Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-n8n-integration.md). This guide walks you through creating API credentials and configuring n8n to work with your site. After completing this task, you can create workflows that react to events on your Wix site, perform Wix actions, connect to other n8n integrations, and use AI agents to power your automations.
**Important:** Wix n8n nodes are currently community nodes. Before you can use them, you must install the `@wix/n8n-nodes-wix` package. See the n8n guide on [installing community nodes](https://docs.n8n.io/integrations/community-nodes/installation/).
## Step 1 | Create an API key in Wix Create an [API key](https://dev.wix.com/docs/api-reference/articles/authentication/about-api-keys.md) in your Wix account with the permissions needed for the triggers and actions you want to use. To create an API key: 1. Go to the [API Keys Manager](https://manage.wix.com/account/api-keys) in your Wix account. 2. Click **Create API Key**. 3. Enter a name for your API key, such as "n8n Integration". 4. Select the permissions for the APIs you want to access. The permissions you need depend on which triggers and actions you plan to use. For example: - For product operations, select **Wix Stores** permissions. - For order operations, select **Wix eCommerce** permissions. 5. Click **Create**. 6. Copy and securely store your API key. You can't view it again.
__Important:__ Store your API key securely and never expose it in frontend code.
## Step 2 | Find your site ID Locate the ID of the Wix site you want to connect to n8n. To find your site ID: 1. Go to your [site's dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fhome). 2. Check the URL in your browser. The site ID appears after `/dashboard/` in the URL. For example, in `https://manage.wix.com/dashboard/12345678-1234-1234-1234-123456789012/home`, the site ID is `12345678-1234-1234-1234-123456789012`. Alternatively, you can use the [Query Sites](https://dev.wix.com/docs/api-reference/account-level/sites/sites/query-sites?apiView=SDK.md) API to retrieve all site IDs associated with your account. ## Step 3 | Configure Wix credentials in n8n Add your Wix API credentials to n8n to authenticate your workflows. To configure credentials: 1. In n8n, go to **Settings > Credentials**. 2. Click **Add Credential**. 3. Search for and select **Wix API**. 4. Enter your credentials: - **API Key**: Paste the API key you created in Step 1. - **Site ID**: Paste the site ID you found in Step 2. 5. Click **Save**. ## Step 4 | Test the connection Verify that your credentials are working by creating a simple workflow. To test the connection: 1. Create a new workflow in n8n. 2. Add a Wix node to the canvas. 3. When configuring the node, select your saved Wix API credentials. 4. Browse the available resources and operations to see what actions you can perform. 5. Select an operation and click **Test step** to execute it. If the connection is successful, you see results from your Wix site. ## See also - [About n8n Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-n8n-integration.md) - [About API Keys](https://dev.wix.com/docs/rest/articles/authentication/about-api-keys.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Zapier Integration ## Article: About Zapier Integration ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-zapier-integration.md ## Article Content: # About Zapier Integration [Zapier](https://zapier.com/) is a workflow automation platform that lets you connect apps and services to create automated workflows called Zaps. With the built-in Wix Zapier integration, you can automate tasks involving a Wix site without writing code, connecting Wix to thousands of other apps available in the Zapier ecosystem. To get started, see [Create a Zap with a Wix Trigger](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/create-a-zap-with-a-wix-trigger.md). ## What you can do with the integration The Wix Zapier integration is powered by [Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/triggers/about-triggers?apiView=SDK.md). It provides instant triggers that activate when events occur on a Wix site, such as form submissions, new orders, and booking confirmations. When a trigger activates, the connected Zap performs actions in other apps. For example, a Zap can add a row to a Google Sheet, send an email, or create a task in a project management tool.
__Tip:__ The Wix integration provides triggers only. To send data from Zapier back to a Wix site, use the [Webhook Received trigger](https://support.wix.com/en/article/wix-automations-setting-up-a-webhook-trigger) in Wix Automations to receive data from a Zapier Zap.
### Available trigger categories The integration supports triggers across many Wix business solutions. Some examples include: | Category | Example triggers | |---|---| | Blog | New blog post published, site visitor comments on a post | | Bookings | Session starts, session ends, client books, client checks in | | eCommerce | Order placed, checkout abandoned | | Events | Event ends, ticket ordered, guest joins the waitlist | | Forms | Form submitted | | Members and Contacts | Contact created, group status changes, post activity | | Payments | Payment received, payment method updated | | Pricing Plans | Plan purchased, canceled, or soon to expire | | Restaurants | Order created, order accepted, delivery order ready | For a complete list of available triggers, see the [Wix integrations page on Zapier](https://zapier.com/apps/wix/integrations#triggers-and-actions). ### Enhance Zaps with Zapier features Zapier provides built-in tools that let you build more sophisticated workflows on top of Wix triggers: - **Multi-step Zaps**: Chain multiple actions in a single Zap. For example, when a Wix Form is submitted, create a CRM contact, add a row to a spreadsheet, and send a Slack notification, all in one workflow. - **Paths**: Add conditional branching to route data based on its content. For example, route form submissions to different email lists based on the submission type. - **Filters**: Set conditions that control whether a Zap continues. For example, only trigger an action when an order total exceeds a certain amount. - **Formatter**: Transform data between steps. For example, format dates, extract text, or convert currencies before passing data to the next action. > **Note:** Some Zapier features, including those listed above, require a paid Zapier plan. ## Prerequisites and authentication To use the Wix Zapier integration, you need: - A [Zapier account](https://zapier.com/sign-up). - A Wix site with the relevant business solution installed, such as Wix Blog or Wix Forms. To connect Zapier to a Wix site, authorize the connection through an OAuth flow. When setting up a Wix Zap for the first time, Zapier prompts for Wix account sign-in and site selection. No API keys or site IDs are required. ## Pre-built templates Zapier provides pre-built templates that let you set up common Wix automations. Templates come pre-configured with a Wix trigger and an action in another app, so you only need to connect your accounts and customize the details. Some popular templates include: - Add or update Mailchimp subscribers when a Wix Form is submitted. - Create HubSpot contacts from Wix Form submissions. - Add rows to Google Sheets when a Wix Form is submitted. - Send Facebook Conversion events from Wix. - Schedule Google Meet meetings for new Wix bookings. - Create items in monday.com from Wix Form submissions. - Send WhatsApp notifications when new forms are received in Wix. - Create Google Ads contacts from Wix Form submissions. Browse all available templates on the [Wix integrations page on Zapier](https://zapier.com/apps/wix/integrations#zap-template-list). ## Use cases Here are some examples of what you can automate with the Wix Zapier integration: - Lead capture and CRM sync: When a site visitor submits a form, automatically create a contact in a CRM and notify a sales team through Slack or email. - Abandoned checkout recovery: When a checkout is abandoned, send a personalized follow-up email through your email marketing platform with a discount code to encourage the customer to complete their purchase. - Booking confirmations: When a client books an appointment, automatically create a calendar event in Google Calendar and send a confirmation message through WhatsApp or SMS. - Order management: When a new eCommerce order is placed, add the order details to a Google Sheet, create a task in your project management tool, and send a notification to your fulfillment team. - Cross-platform marketing: When a new blog post is published, automatically share it to your social media accounts and notify your email subscribers. ## See also - [Create a Zap with a Wix Trigger](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/create-a-zap-with-a-wix-trigger.md) - [Zapier Wix integrations page](https://zapier.com/apps/wix/integrations) - [Third-Party Apps: Wix Triggers on Zapier](https://support.wix.com/en/article/third-party-apps-zapier-by-zapier) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Zap with a Wix Trigger ## Article: Create a Zap with a Wix Trigger ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/create-a-zap-with-a-wix-trigger.md ## Article Content: # Create a Zap with a Wix Trigger Create an automated workflow in Zapier that starts when an event occurs on a Wix site. For an overview of the Wix Zapier integration and what you can do with it, see [About Zapier Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-zapier-integration.md). This guide walks you through creating a Zap that uses a Wix trigger to start an automated workflow and connects it to an action in another app. ## Step 1 | Create a Zap and connect a Wix account This step creates a new Zap, selects Wix as the trigger app, and authenticates with a Wix account. To create a Zap and connect a Wix account: 1. Go to [zapier.com](https://zapier.com/) and click **+ Create** > **Zaps**. 2. In the Zapier editor, click the trigger step and select **Wix** as the trigger app. 3. In the **Trigger event** dropdown, select a trigger category. For example, select **Blog** for blog-related triggers, or **Forms** for form submission triggers. 4. In the **Account** section, connect a Wix account: - If you've previously connected a Wix account, click **Select** and choose an account from the list. - If you're connecting for the first time, click **Connect a new account**. This opens an OAuth window where you sign in to your Wix account and select a site to connect. 5. Click **Continue**. ## Step 2 | Configure and test the trigger This step selects the specific event that starts the Zap and verifies the connection by retrieving sample data from the Wix site. To configure and test the trigger: 1. In the **Select the specific trigger to use** dropdown, select the specific event that starts the Zap. For example, if you selected **Blog** in the previous step, you can select **New blog post published** or **Blog post liked**.
__Note:__ Zapier displays a note if a specific Wix app is required for the trigger to work. Make sure the app is installed on your site.
1. Click **Continue**. 1. Click **Test trigger**. Zapier checks the connection and retrieves sample data from the Wix site. 1. Review the sample data to confirm it looks correct. 1. Click **Continue with selected record** to proceed. ## Step 3 | Add an action This step connects another app to the Zap and configures it to respond to the Wix trigger. The action is the task that runs automatically whenever the trigger event occurs. To add an action: 1. Click the action step. 1. Search for and select the app you want to connect. For example, select **Google Sheets** to log data to a spreadsheet, or **Slack** to send notifications. 1. In the **Action event** dropdown, select the action to perform. For example, select **Create Spreadsheet Row** for Google Sheets. 1. Connect your account for the action app and then click **Continue**. 1. Configure the action fields: 1. Select the app-specific settings, such as which spreadsheet and worksheet to use. 1. Map the Wix trigger data to the remaining fields. Click in a field and select from the available trigger data to pass it to the action. 1. Click **Continue** and test the action to verify it works. ## Step 4 | Publish the Zap To publish the Zap: 1. Click **Publish** to activate the Zap so it runs automatically whenever the trigger event occurs on the Wix site. ## See also - [About Zapier Integration](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/integrations/about-zapier-integration.md) - [Zapier Wix integrations page](https://zapier.com/apps/wix/integrations) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Identities ## Article: About Identities ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-identities.md ## Article Content: # About Identities When you call a method on a site or on a [custom dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md), it's called by a specific identity. This identity represents the current site user who triggers the method call, regardless of whether the code making the call is in the frontend, the backend, or on a custom dashboard page. Some Wix methods are restricted based on which identities are authorized to call them. Authorization notes in the API reference indicate when a method may be restricted by identity. If necessary, you can call such methods even when the current identity lacks permissions by using [elevation](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-elevation.md). > **Note:** Some methods may further restrict access by requiring that the calling identity has specific permissions granted through a role. You can use elevation to bypass these restrictions as well. Wix recognizes the following identities: - [Site visitor](#site-visitor) - [Site member](#site-member) - [Wix user](#wix-user) ## Site visitor Site visitors are visitors to a site who have not logged in. Methods that site visitors can call typically involve operations specific to their own experience on the site, such as: - Adding products to the current cart. - Creating a booking. - Copying text to the local clipboard. ## Site member Site members are visitors who have logged in. They can call all the same methods available to site visitors, plus additional methods that only apply to their site membership. Site members who are also [site owners or collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site) are also considered admins. Their admin status allows them to call certain administrative methods that other members can't call, such as methods to work with the files in the site's Media Manager. ## Wix user When interacting with the site dashboard, site owners or collaborators take on the Wix user identity. Although Wix users are the same people as site owners and collaborators, they assume different identities depending on context. As the dashboard is a secured admin area, Wix users can call methods with stricter authorization requirements in the dashboard. Examples of administrative methods Wix users can call in the dashboard include: - Adding products to a store. - Confirming or canceling bookings. - Approving new members. > **Note:** Although calls made from a published dashboard page are called with the authorization of a Wix user, those same calls made from a dashboard page being previewed in the editor are only called with the authorization of a site member admin. This means that calls which will work in your published dashboard page may not work when previewing the same dashboard page. ## See also - [About Elevation](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-elevation.md) - [Roles & Permissions](https://support.wix.com/en/article/roles-permissions-overview) - [elevate()](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md#elevate) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Elevation ## Article: About Elevation ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-elevation.md ## Article Content: # About Elevation > **Note:** This article discusses elevation when developing websites, but the concepts and implementation are the same when [coding in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/code-in-blocks/about-coding-in-blocks.md). Some Wix methods are restricted based on the [identities](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-identities.md) and [roles](https://support.wix.com/en/article/roles-permissions-overview) authorized to call them. Elevation is a mechanism that allows you to call these restricted methods even when the calling identity lacks the necessary authorization. This process creates a temporary elevated version of a method by calling the `elevate()` method, enabling you to bypass the usual authentication checks that might otherwise restrict access. For example: ```javascript import { auth } from "@wix/essentials"; import { someModule } from "@wix/some-module"; //... const elevatedMethod = auth.elevate(someModule.methodName); elevatedMethod(param1, param2); ``` ## Methods that may require elevation Methods can be restricted based on user [identity](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/authorization/identities.md) or [roles and permissions](https://support.wix.com/en/article/roles-permissions-overview). ### Identity restriction example An example of a method restricted by identity is the [`assignBadge()`](https://dev.wix.com/docs/sdk/backend-modules/members/badges/assign-badge.md) method. This method can only be called by [Wix users](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-identities.md#wix-user) because site members shouldn't be able to assign badges to themselves. This means that `assignBadge()` can only be called without elevation in a published dashboard page, where the caller will have the Wix user identity. In all other cases, if you need to call `assignBadge()`, you must elevate it first. For example, you might want to automatically assign a badge to users who take some specific action or reach a predetermined milestone. Since the site members who earned the badge don't have rights to assign it to themselves. In this case, you need to use elevation to assign the badge. ### Roles and permissions restriction example An example of a method restricted based on roles and permissions is the [`confirmBooking()`](https://dev.wix.com/docs/sdk/backend-modules/bookings/bookings/confirm-booking.md) method. This method can only be called by admin members with an administrative bookings role because users creating bookings shouldn't be allowed to confirm their own bookings. If you need to call `confirmBooking()` on behalf of a user who does not have an administrative Bookings role, you must elevate it first. For example, you might have a specific service that you want to confirm automatically once a site visitor books it. Since site visitors who book the service don't have permissions to confirm it themselves, you need to use elevation to confirm the booking. ## Security considerations Due to potential security risks, you can only elevate methods in backend code. While elevation offers flexibility, it's crucial to consider how and when elevation is triggered. [Web methods](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/web-modules/about-web-modules.md) and [HTTP functions](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/expose-services/about-custom-site-apis.md) are particularly vulnerable if not properly managed due to their open nature. Elevation in [backend events](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/backend-events/about-backend-events.md) or code only triggered from [scheduled jobs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/scheduled-jobs/about-scheduled-jobs.md) presents less risk but should still be handled cautiously. ### Example To demonstrate how to properly handle elevation, consider a site that rewards frequent visitors with a special badge. To do so, the code needs to track recent member visits and call `assignBadge()` using elevation when a member has visited frequently enough. (Note that the code samples below have been simplified, removing error handling and other non-essential code.) Here is an example of an insecure approach to writing this code: ```javascript // Backend code in badges.web.js import { auth } from "@wix/essentials"; import { webMethod, Permissions } from "@wix/web-methods"; import { badges } from "@wix/members"; export const assignBadge = webMethod( Permissions.Anyone, (badgeId, memberId) => { const elevatedAssignBadge = auth.elevate(badges.assignBadge); return elevatedAssignBadge(badgeId, [memberId]); } ); export const isFrequentVisitor = webMethod(Permissions.Anyone, (memberId) => { // Query collection that tracks member visits, // determine if the specified member is a frequent visitor, // and return the result }); // Frontend code in masterPage.js import { assignBadge, isFrequentVisitor } from "backend/badges.web"; import { members } from "@wix/members"; const frequentVisitorBadgeId = "c705b8dd-aae2-4eea-a4d1-16f52421ec0a"; //... const currentMember = await members.getCurrentMember(); if (isFrequentVisitor(currentMember._id)) { assignBadge(frequentVisitorBadgeId, currentMember._id); } ``` There are several problems with the `assignBadge()` web method used in this approach: - It is open for anyone to call, even though only members can receive badges. - It doesn’t ensure that it will only assign the intended badge. - It doesn’t ensure that it will assign a badge to the currently logged in member. Because of these issues, this method can be called by a malicious user to assign any badge to any member. You can easily remedy these issues by being more careful about where you use elevation and how you expose it to be called. For example: ```javascript // In badges.web.js import { auth } from "@wix/essentials"; import { Permissions, webMethod } from "wix-web-module"; import { badges } from "@wix/members"; import { members } from '@wix/members'; const assignFrequentVisitorBadge = webMethod( Permissions.Member, async () => { const currentMember = await members.getCurrentMember(); if (isFrequentVisitor(currentMember._id)) { const frequentVisitorBadgeId = 'c705b8dd-aae2-4eea-a4d1-16f52421ec0a'; const elevatedAssignBadge = auth.elevate(badges.assignBadge); return elevatedAssignBadge(frequentVisitorBadgeId, [currentMember._id]); } } ) const isFrequentVisitor = (memberId) => { // Query collection that tracks member visits, // determine if the specified member is a frequent visitor, // and return the result } // masterPage.js import { assignFrequentVisitorBadge } from 'backend/badges.web'; //... assignFrequentVisitorBadge(); ``` In this approach, the following makes sure the elevation isn't exploited by malicious users: - The ID of the badge to assign is specified in backend code. - The current user ID is retrieved in backend code. - The web method used to trigger the badge assignment has permissions set so it can only be called by site members. ## See also - [About Identities](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/authorization/about-identities.md) - [Roles & Permissions](https://support.wix.com/en/article/roles-permissions-overview) - [elevate()](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md#elevate) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Event Handlers ## Article: About Event Handlers ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md ## Article Content: # About Event Handlers Frontend event handlers in Wix work differently than in vanilla JavaScript. Normally, you would use `addEventListener()` to add event handlers, selecting DOM elements with methods like `getElementById()` or `querySelector()`. For example, you might write: ```js document.getElementById("myButton").addEventListener("click", function () { console.log("Button clicked!"); }); ``` In Wix, however, you don't access the DOM directly. Instead, you use the [$w API](https://dev.wix.com/docs/velo/velo-only-apis/$w/introduction.md) to interact with elements in the Wix Editor and add event handlers, like this: ```js $w("#myButton").onClick(() => { console.log("Button clicked!"); }); ``` ## Using SDK code in event handlers Although you need to create event handlers using the Velo API, you can use SDK code in the event handler body. For example: ```js import { items } from "@wix/data"; // Add an event handler using Velo $w("#submitButton").onClick(async () => { // Use SDK code inside the event handler const result = await items.insert("listings", { title: $w("#titleInput").value, price: Number($w("#priceInput").value), bedrooms: Number($w("#bedroomsInput").value), }); }); ``` ## Supported IDEs You can use event handlers in the following IDEs: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor)
Important: Wix's static event handlers have been deprecated. While [static event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-static-event-handlers.md) are still supported for existing sites, we recommend using dynamic event handlers for new development and when updating existing code.
## See also - [Add an Event Handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/add-an-event-handler.md) - [About Code Placement](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/code-with-java-script/about-code-placement.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add an Event Handler ## Article: Add an Event Handler ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/add-an-event-handler.md ## Article Content: # Add an Event Handler You can add [dynamic event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) to elements on a site to respond to visitor interactions like clicks, mouse movements, or form submissions. To add an event handler to an element, do the following either inside or outside of the page's [`onReady()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/on-ready.md) handler: 1. Use the `$w` API to select the element you want to add an event handler to. 1. Use the appropriate event method, such as `onClick()`, `onMouseIn()`, or `onChange()`, to define the event handler. 1. Write the code you want to execute when the event occurs inside the event handler method body. For example, the following code adds a click event handler to a button that changes the text of a text element when clicked: ```js $w("#myButton").onClick((event) => { $w("#myText").text = "Hello, World!"; }); ``` > **Note:** You can also add event handlers using these alternative methods: > > - Use the Properties & Events panel in the Wix Editor to generate the event handler registration code that you add the implementation to. > - Use the [AI Code Assistant](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ai-assistants/about-ai-assistants.md#ai-code-assistant-in-the-wix-studio-code-panel) in Wix Studio to generate custom event handler code. ## See also - [About Event Handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) - [About the Properties & Events Panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Static Event Handlers ## Article: About Static Event Handlers ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-static-event-handlers.md ## Article Content: # About Static Event Handlers
Deprecation Notice: This feature is deprecated but will continue to work as expected. Switch to [dynamic event handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) for current and future projects.
A static event handler relies on the static wiring of the element to a handler function. The event handler can't be dynamically added or modified at runtime. ## Migrate from static to dynamic event handlers To migrate a static event handler to a dynamic one: 1. Open the Properties & Events panel for the element with the static event handler. 2. Find the event you want to migrate and click the yellow lightning bolt icon next to the event handler name. 3. Click the **Migrate event** link that appears. After migration, your code updates from the old static format to the new dynamic format. For example: **Old format:** ```js export function myButton_click(event) { // Existing code } ``` **New format:** ```js $w("#myButton").onClick((event) => { // Existing code }); ``` ## Delete a static event handler You can no longer use the Properties & Events panel to delete static event handlers. To delete a static event handler, delete the generated code from the page code file. ## See also - [About Event Handlers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/about-event-handlers.md) - [Add an Event Handler](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/event-handlers/add-an-event-handler.md) - [About the Properties & Events Panel](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-properties-events-panel.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom CSS ## Article: About Custom CSS ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md ## Article Content: # About Custom CSS CSS allows you to customize the appearance of a site, from colors and fonts to sizes and positions of different elements. You can even add design elements that aren't yet available in the editor. For example, you can: - Create a blur filter and apply it to a site element. - Use mix-blend-mode properties to combine different elements. - Add a gradient color effect to text. ## Supported IDEs You can customize the CSS using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) ## Classes Classes group elements on a site together to easily apply the same styling to all the instances of that group. You can apply custom CSS styling to a global class, semantic class, or custom class. ### Global classes Global classes are built-in and made available to a site. Applying a style to a global class affects the CSS styling of elements of that class globally, throughout the site. To get started, see the [list of available classes](https://dev.wix.com/docs/velo/velo-only-apis/$w/styling-elements-with-css.md#available-classes). ### Semantic classes Most global classes are divided into more specific semantic classes to customize individual aspects. For example, you can apply a custom style to all buttons with `.button` or only apply a style to the label text of a button with `.button__label`. See the [documentation of each element](https://dev.wix.com/docs/velo/velo-only-apis/$w/styling-elements-with-css.md#available-classes) for specific information on supported semantic classes. ### Custom classes In addition to the built-in, global classes you can also create custom classes and apply CSS styling to just those. For example, you may want to apply a certain animation style to all subscribe buttons throughout a site, but not to all buttons in general. To do so, you can create a custom class called `.button--subscribe` and add custom styling for all elements that you add this class to. ## See also - For further information on CSS styling options, refer to the [MDN CSS reference docs](https://developer.mozilla.org/en-US/docs/Learn/CSS) - [Wix Studio webinar: Enhance your designs with CSS editing](https://www.youtube.com/watch?v=xynm4m83CqM) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Apply Custom CSS Styles ## Article: Apply Custom CSS Styles ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/apply-custom-css-styles.md ## Article Content: # Apply Custom CSS Styles You can apply custom [CSS styling](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md) to any supported element on a site. ## Step 1 | Add the global.css file Add a `global.css` file. ::::tabs :::Wix-Studio 1. Navigate to **Page Code** in the Code sidebar. 2. In the **CSS** section, click **+ Add global.css**. ::: :::Wix-IDE-or-Local-IDE Add a `global.css` file to the `src/styles` folder. ::: :::: ## Step 2 | (Optional) Add a custom class to a site In addition to the [available classes](https://dev.wix.com/docs/velo/velo-only-apis/$w/styling-elements-with-css.md#available-classes) you can add a new class to any supported element. ::::tabs :::Code Add 1 or more classes to the element with the `add()` method: ```javascript $w("#myElement").customClassList.add("className1", "className2"); ``` ::: :::Wix-Studio-Editor 1. Select an element. 2. In the CSS Classes panel, type your custom class in the Custom classes field: ![custom classes panel in editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d8f840d04075e4169aee11d653dc1dfb.png) ::: :::: ## Step 3 | Apply custom styles to the class In the [`global.css` file](#step-1--add-the-globalcss-file) add custom CSS styles to apply. For example: ```css .accordion { text-transform: capitalize; color: yellow; } .button--subscribe { background: linear-gradient(to bottom right, red, yellow); } ``` You can also use [site theme CSS variables](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/site-theme-css-variables.md) to apply the site's theme colors and fonts to the class. For example: ```css .container { background-color: var(--wst-color-fill-background-primary); } ``` ## See also - [About Custom CSS](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md) - [Webinar: Enhance your designs with CSS editing](https://www.wix.com/studio/academy/webinars/enhance-your-designs-with-css-editing) - For specific examples of available classes, refer to the [API documentation](https://dev.wix.com/docs/velo/velo-only-apis/$w/styling-elements-with-css.md#available-classes) - For further information on CSS styling options, refer to the [MDN CSS reference docs](https://developer.mozilla.org/en-US/docs/Learn/CSS) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Site Theme CSS Variables ## Article: Site Theme CSS Variables ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/site-theme-css-variables.md ## Article Content: # Site Theme CSS Variables Wix exposes a comprehensive set of CSS variables a site's theme colors and fonts. You can use these variables when applying custom [CSS styling](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/css/about-custom-css.md) to supported elements on a site. These variables represent the site's color and typography styles, enabling dynamic and consistent design across all components. Use this reference to identify and apply the correct variables for colors and fonts in code. ## Color theme ### General | Style | CSS Variable | | -------------------- | --------------------------------------- | | Primary background | `--wst-color-fill-background-primary` | | Secondary background | `--wst-color-fill-background-secondary` | | Lines and dividers | `--wst-color-line` | ### Text | Style | CSS Variable | | ----------------- | ---------------------------- | | Title | `--wst-color-title` | | Subtitle | `--wst-color-subtitle` | | Body text | `--wst-color-text-primary` | | Secondary text | `--wst-color-text-secondary` | | Links and actions | `--wst-color-action` | | Disabled actions | `--wst-color-disabled` | ### Buttons #### Primary button | Style | CSS Variable | | ----------------- | -------------------------------------------- | | Fill (Regular) | `--wst-button-color-fill-primary` | | Border (Regular) | `--wst-button-color-border-primary` | | Text (Regular) | `--wst-button-color-text-primary` | | Fill (Hover) | `--wst-button-color-fill-primary-hover` | | Border (Hover) | `--wst-button-color-border-primary-hover` | | Text (Hover) | `--wst-button-color-text-primary-hover` | | Fill (Disabled) | `--wst-button-color-fill-primary-disabled` | | Border (Disabled) | `--wst-button-color-border-primary-disabled` | | Text (Disabled) | `--wst-button-color-text-primary-disabled` | #### Secondary button | Style | CSS Variable | | ----------------- | ---------------------------------------------- | | Fill (Regular) | `--wst-button-color-fill-secondary` | | Border (Regular) | `--wst-button-color-border-secondary` | | Text (Regular) | `--wst-button-color-text-secondary` | | Fill (Hover) | `--wst-button-color-fill-secondary-hover` | | Border (Hover) | `--wst-button-color-border-secondary-hover` | | Text (Hover) | `--wst-button-color-text-secondary-hover` | | Fill (Disabled) | `--wst-button-color-fill-secondary-disabled` | | Border (Disabled) | `--wst-button-color-border-secondary-disabled` | | Text (Disabled) | `--wst-button-color-text-secondary-disabled` | ### Theme colors | Style | CSS Variable | | -------- | ------------------------------- | | Base 1 | `--wst-color-fill-base-1` | | Base 2 | `--wst-color-fill-base-2` | | Shade 1 | `--wst-color-fill-base-shade-1` | | Shade 2 | `--wst-color-fill-base-shade-2` | | Shade 3 | `--wst-color-fill-base-shade-3` | | Accent 1 | `--wst-color-fill-accent-1` | | Accent 2 | `--wst-color-fill-accent-2` | | Accent 3 | `--wst-color-fill-accent-3` | | Accent 4 | `--wst-color-fill-accent-4` | ## Font theme | Style | CSS Variable | | ------------------------------------------- | ------------------------------ | | Heading 1 titles (reserved for page titles) | `--wst-font-style-h1` | | Heading 2 titles (recommended) | `--wst-font-style-h2` | | Heading 3 titles | `--wst-font-style-h3` | | Heading 4 titles | `--wst-font-style-h4` | | Heading 5 titles | `--wst-font-style-h5` | | Heading 6 titles | `--wst-font-style-h6` | | Large body text (paragraph 1) | `--wst-font-style-body-large` | | Medium body text (paragraph 2; recommended) | `--wst-font-style-body-medium` | | Small body text (paragraph 3) | `--wst-font-style-body-small` | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Element Size Changes and Page Layout ## Article: About Element Size Changes and Page Layout ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-elements/about-element-size-changes-and-page-layout.md ## Article Content: # About Element Size Changes and Page Layout
**Important:** This article is only for Wix Editor sites. The way elements interact with each other in Wix Studio may differ. Learn more about [Wix Studio page structure](https://support.wix.com/en/article/studio-editor-about-page-structure-parent-child-and-sibling-elements).
Element resizing in the Wix Editor occurs when elements change size automatically. For example, a text box might expand or a repeater might grow as more items appear. These changes can shift other elements on the page and affect overall page height. Understanding how the Wix Editor positions elements and reacts to size changes helps you predict and control page behavior. ## Why elements shift on a page When elements change size, other elements on the page may shift to maintain spacing based on the gap rules defined below. Elements may shift due to: - Other elements [collapsing or expanding](#collapse-and-expand). - Other elements [growing](#elements-that-grow) to accommodate more content. Elements don't shift due to: - Animations. - Other elements being hidden or shown. ## When this matters The layout rules are particularly important when you're building: - **Dynamic content features**. For example: - Adding or removing items from [repeaters](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/introduction.md). - Growing [text elements](https://dev.wix.com/docs/velo/velo-only-apis/$w/text/introduction.md) with user input. - Loading content via a [dataset](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md) that varies in size. - Displaying search results with varying content. - Displaying user-generated content that changes over time. - **Interactive elements**. For example: - Collapsible elements that site visitors can collapse and expand. - [Wix Forms](https://support.wix.com/en/article/wix-forms-an-overview) that show/hide fields conditionally. - [Multi-state boxes](https://dev.wix.com/docs/velo/velo-only-apis/$w/multi-state-box/introduction.md) with states of different sizes. For example: ```javascript $w('#myText').text = 'Much longer text that will increase the element height'; // Elements below automatically adjust based on the rules. ``` ## Element hierarchy The Wix Editor organizes content as nested containers and elements. A parent contains its children, and elements with the same parent are siblings at the same level. Parents set overall context and constraints, but a parent's effective bounds are often determined by its children. For example, a container can expand to fit new content. Top‑level elements typically include the [Header](https://dev.wix.com/docs/velo/velo-only-apis/$w/header/introduction.md), the Page (which includes [Sections](https://dev.wix.com/docs/velo/velo-only-apis/$w/section/introduction.md)), and the [Footer](https://dev.wix.com/docs/velo/velo-only-apis/$w/footer/introduction.md), while their child elements can vary in many ways. The diagram below shows a possible arrangement of elements: ![Element Hierarchy](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4fa6953012e8d77fa79c1f81157f627e.png "Element Hierarchy") ## Scope of the rules These layout rules consider only vertical position and size. The system ignores horizontal positioning when determining which elements affect each other. ### Vertical gaps The **vertical gap** between elements is the empty space between them, measured from the bottom of the higher element to the top of the lower element. When deciding whether to trigger movement, the system always checks the element with the smallest gap from the resized element. This article refers to this element as **the nearest sibling**.
How to calculate the gap You need the exact size and position of each element. You can view these values in the [Wix Editor Toolbar](https://support.wix.com/en/article/using-your-editor-toolbar). To calculate the gap: 1. Calculate the Y value of the bottom edge of the higher element by adding its H (the height) to its Y value. 1. Calculate the gap by subtracting that bottom edge value from the Y value of the lower element. For example, in the illustration below: - The blue element is positioned at 150 px and has a height of 50 px, so its bottom edge is at 200 px, calculated as 150 + 50. The orange element is at 250 px, so the gap is 50 px, calculated as 250 - 200. - The same process shows that the gap between the orange and green elements is 100 px. ![Calculating the Gap](https://wixmp-833713b177cebf373f611808.wixmp.com/images/aeb1fb59ce75d83123f9f47c562eb3fe.png "Calculating the Gap")
### Rule evaluation sequence When an element changes size, the system evaluates movement in this order: 1. In the same container (siblings): Finds the nearest lower sibling by the smallest vertical gap and applies the relevant rule. Continues down the sibling chain until no more siblings need to move. 1. Container adjustment (parent): If the parent container needs more or less space to fit its children, it expands or collapses from the bottom accordingly. The children keep their relative positions. 1. Outside the container (parent's siblings): Treats the parent as a single element relative to its own nearest lower sibling and continues evaluation down that chain. 1. Repeats up the hierarchy as needed until no more elements move. > **Note:** > If multiple elements have the same smallest gap, the system treats them as equally nearest. > In that case, all of those elements move at the same time according to the applicable rules. For clarification, each of the following is treated as a single element when evaluated against its siblings: - Single elements ([Text](https://dev.wix.com/docs/velo/velo-only-apis/$w/text/introduction.md), [Image](https://dev.wix.com/docs/velo/velo-only-apis/$w/image/introduction.md), [Button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md), and more) - [Container Box](https://dev.wix.com/docs/velo/velo-only-apis/$w/box/introduction.md) and Group - [Header](https://dev.wix.com/docs/velo/velo-only-apis/$w/header/introduction.md), [Section](https://dev.wix.com/docs/velo/velo-only-apis/$w/section/introduction.md), and [Footer](https://dev.wix.com/docs/velo/velo-only-apis/$w/footer/introduction.md)
**Tip:** To keep multiple items moving together, group them together or place them inside a container box, which you can also set to transparent.
> **Note:** > A group's bounds (top, bottom, left, and right) are derived from its children and determine gaps and overlap. The top equals the highest child's top position, and the same principle applies to the other bounds. To learn more, see [Grouping and Ungrouping Elements](https://support.wix.com/en/article/wix-editor-grouping-and-ungrouping-elements). ## Rules The rules below outline how the system evaluates and applies layout adjustments. When an element changes size, movement can cascade through the layout. The system repeatedly applies the evaluation order described in [Rule evaluation sequence](#rule-evaluation-sequence) until no more elements move. ### Collapse and expand When an element collapses or expands, elements below may shift vertically. The behavior depends on the size of the gap between elements: #### Gaps of 70 px or less Elements below move: - If the element has collapsed, they shift up to fill the space. - If the element has expanded, they move down to maintain the gap. #### Gaps greater than 70 px Elements below stay in place, regardless of whether the element above has collapsed or expanded.
Example: A 50 px gap and a 100 px gap ![Collapsing Element Example 1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0f25119a494d1a4c060443334ff15b6e.png "Collapsing Element Example 1") In the illustration above, the gap between the blue and orange elements is 50 px. Since this gap is less than 70 px, when the blue element collapses, the orange element moves up. However, the gap between the orange and green elements is 100 px. Since this gap is greater than 70 px, the green element stays in place.
Example: A 50 px gap and another 50 px gap ![Collapsing Element Example 2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b2d7e51f9b2457b465c5714933e7bb9e.png "Collapsing Element Example 2") In this example, both gaps are 50 px. Consequently, when the blue element collapses, both the orange and green elements move up into the space previously occupied by the blue and orange elements, respectively.
### Elements that grow The height of an element can increase when its content increases. For example, elements connected to a dataset may need to expand to display additional information. When an element grows, the elements below may move down to accommodate the extra space. This behavior depends on the size of the gap between elements: #### Gaps of 70 px or less Elements below move down to preserve the original gap. Their new Y value increases by the gap size. #### Gaps greater than 70 px Elements below stay in place. The exception is when the resulting gap falls below 10 px, in which case the lower element moves down to maintain a 10 px gap.
Example: A 50 px gap ![Elements that Grow Example 1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/adc24a146c49fa44618a2b2a43b8ed53.png "Elements that Grow Example 1") In this example, the gap between the blue and orange elements is 50 px. Since this gap is 70 px or less, when the blue element grows by 30 px, the orange element moves down by 30 px to preserve the 50 px gap.
Example: An 80 px gap ![Elements that Grow Example 2](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bd8510209f802c1d0306dfa9085cad0a.png "Elements that Grow Example 2") In this example, the gap between the blue and orange elements is 80 px. Since this gap is greater than 70 px: - In the first scenario, when the blue element grows by 30 px, the orange element stays in place because the resulting gap is 50 px, which is above the 10 px minimum. - In the second scenario, when the blue element grows by 74 px, the orange element moves down by 4 px to maintain the 10 px minimum, because the resulting gap would otherwise be 6 px.
## Vertical overlap exception Two elements are considered to vertically overlap when the lower element's top edge sits above the vertical middle of the higher element. > **Note:** > An element's vertical middle is the average of its top and bottom edges. Wix Editor calculates it as Y + H/2, where Y is the top position and H is the height. When elements overlap vertically, the normal [rules](#rules) don't fully apply. For example, the behavior for 2 vertically overlapping elements during collapse and expand works as follows: - **Single change**: If only 1 of the 2 overlapping elements collapses or expands, no other elements react. - **Both change**: If both overlapping elements collapse or expand, the normal hierarchy evaluation applies as if only the lower of the overlapping elements changed. The illustration below demonstrates this behavior: ![Overlapping Elements Example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c8c431cc93f78cbf0b27b52e0522d0d7.png "Overlapping Elements Example") In both scenarios, the orange element's top edge is above the blue element's vertical middle. Therefore, overlap rules apply: - In the first scenario, only the blue element collapses. It's a single change, therefore it doesn't cause other elements to move. - In the second scenario, both the blue and orange elements collapse. The green element then moves up to take the orange element's position.
Example: usual behavior The illustration below demonstrates 2 scenarios where elements aren't considered overlapping: ![Non-Overlapping Elements Example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ed6ff1a45c42cf6ca75a13ec3730e272.png "Non-Overlapping Elements Example") In both scenarios, the orange element's top edge isn't above the blue element's vertical middle, so normal rules apply and elements move as expected. The only difference is that in the lower scenario the blue and green elements overlap both horizontally and vertically after movement, while in the upper scenario they don't.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Forms ## Article: About Forms ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/forms/about-forms.md ## Article Content: # About Forms Forms are interactive parts of your website that let visitors enter and submit information, such as contact details, feedback, or survey responses. With forms on your Wix site, you can easily collect and manage valuable information from your site visitors. ## Ways to work with forms You can work with forms on your Wix site in 2 different ways: - [Wix Forms](#wix-forms): Create forms using [Wix Forms](https://support.wix.com/en/article/about-wix-forms) for quick setup with professional templates. - [Custom forms](#custom-forms): Build your own forms using code and page elements. ### Wix Forms Wix Forms is ideal when you need a fast, reliable solution with professional features and minimal setup. You can use Wix Forms to set up your forms quickly and efficiently and take advantage of useful features like: - A variety of form designs and templates to choose from - Easy customization of form layout in the editor - Automatic email notifications that save information to your contacts and go through the Inbox (Wix CRM) - Built-in support for payment forms, multi-step forms, and subscription forms - An out-of-the-box submission button - Auto-generated emails after submission When you add a Wix form to your site you automatically get a [starter collection](https://support.wix.com/en/article/cms-about-wix-form-collections). Depending on the form you pick, you get a collection specifically built for that form. The form fields are connected to the collection fields, so each time a visitor submits the form, their data is automatically stored in the corresponding collection field. You can view form submissions in the **Wix Form Collections** section in the **CMS** tab on your site's dashboard, or from the sidebar in the editor. For greater flexibility, you can also use the [Forms API](https://dev.wix.com/docs/sdk/backend-modules/forms/introduction.md) to customize the form functionality. For example, you can display a personalized thank you message when a visitor submits the form. > > **Note:** Without a Premium Plan, the Wix Forms app has limited features and capabilities. Learn more about [upgrading your Wix Forms app with a Premium Plan](https://support.wix.com/en/article/wix-forms-upgrading-wix-forms-with-a-premium-plan). ### Custom forms If you want a fully customized form, you can create your own by manually adding user input elements and buttons to your page. You can then use code to handle form submissions and add custom functionality. You can handle the data however you choose. For example, save it to a collection using datasets or the Data Items API, send it to external services, or use it for calculations without storing it. While building custom forms with code takes more time and effort than using Wix Forms, it offers several advantages such as: - Complete control over form design and layout - Custom validations - Advanced third-party API integrations ## See more - [Tutorial: Customize a Wix Form](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/user-interface/tutorial-customize-a-wix-form.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Page Rendering ## Article: About Page Rendering ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-page-rendering.md ## Article Content: # About Page Rendering Page rendering is when your site's code is converted to a visual layout that's displayed in the browser. It occurs when visitors first enter your site, and for each subsequent page load, as they access different pages within the same site. To enhance performance, Wix render's your site code on the server and in the browser. You can use the [Rendering API](https://dev.wix.com/docs/sdk/frontend-modules/window/rendering/introduction.md) to control where your code runs in the rendering process. ## Supported IDEs You can implement the Rendering API using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) ## Rendering process When a page request is made to the server, the server executes server-side rendering and once completed returns the HTML and all the necessary data to render the page. Once the browser gets the server response it displays the HTML immediately so the page is fully visible, though not yet responsive. Then, the browser runs the same page code again, adding the interactive actions. This double rendering occurs on initial site load, first on the server and then in the browser. Navigating to different pages on the same site, however, typically renders only once in the browser for each page load. As a result of the double rendering, any code in your `onReady()` function may often execute twice, to provide faster initial loading time to access a site. ## Side effects Because your `onReady()` runs twice during double rendering, it may produce side effects. For example, if your code inserts an item into a collection, that insertion occurs twice, unless you explicitly add code to prevent that from happening. To avoid such unwanted side effects, use the [Rendering API](https://dev.wix.com/docs/sdk/frontend-modules/window/rendering/introduction.md) to conditionally control what code runs on the server or browser. You can also use [warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-warmup-data.md) to further optimize page loading. ## Test your code Keep the following in mind when testing code that uses the Rendering API. - Rendering never occurs in the server when [previewing](https://support.wix.com/en/article/studio-editor-saving-previewing-and-publishing-your-site#previewing-a-site) your site. - You can't see logs produced by `console.log()` calls in the Developer Console or in your browser's Developer Tools when they're performed on the server. ## See also - [About warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-warmup-data.md) - [Optimize page rendering with warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/optimize-page-rendering-with-warmup-data.md) - [Webinar: Planning for scale: large data design considerations](https://youtu.be/BGnpQ_LTHPg?feature=shared&t=1464) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Optimize Page Rendering with Warmup Data ## Article: Optimize Page Rendering with Warmup Data ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/optimize-page-rendering-with-warmup-data.md ## Article Content: # Optimize Page Rendering with Warmup Data Use [warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-warmup-data.md) to speed up your site's [page rendering](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-page-rendering.md) by reducing the number of data operations that occur during client-side rendering. ## Create a data query with warmup data Follow this procedure to make a data query that's called only once during server-side rendering. The query results are sent as warmup data for display during client-side rendering. If the data query wasn't made during server-side rendering, it's called for the first time during client-side rendering. 1. Add your import statements. ```javascript import { items } from '@wix/data'; import { window } from '@wix/site-window'; ``` 2. Define a `getData()` function that makes a data query. Use the [Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md) to conditionally set the query results as warmup data if the query is being made during server-side rendering. ```javascript async function getData() { const results = await items.query("myCollection").find(); if ((await window.rendering()).env == "backend") { await window.warmupData().set("myWarmupData", results.items); } return results; } ``` 3. In your `onReady()` code, store your data in a variable called `dataResults`. Retrieve the warmup data by calling `warmupData.get`. If server-side rendering doesn't occur, `dataResults` should default to the return value of `getData()`. ```javascript $w.onReady(async function () { const warmupData = await window.warmupData(); const dataResults = warmupData.get("myWarmupData") || await getData(); // Use your data as needed. }); ``` In this example, the `dataResults` variable is populated with the results of the query in all cases. When possible, we've optimized performance by using the warmup data initially fetched on the server, instead of running the query a second time during client-side rendering. The full page code looks like this: ```javascript import { items } from '@wix/data'; import { window } from '@wix/site-window'; async function getData() { const results = await items.query("myCollection").find(); if ((await window.rendering()).env == "backend") { await window.warmupData().set("myWarmupData", results.items); } return results; } $w.onReady(async function () { const warmupData = await window.warmupData(); const dataResults = warmupData.get("myWarmupData") || await getData(); // Use your data as needed. }); ``` ## See also - [About page rendering](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-page-rendering.md) - [About warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-warmup-data.md) - [Warmup Data API](https://dev.wix.com/docs/sdk/frontend-modules/window/warmup-data/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Warmup Data ## Article: About Warmup Data ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-warmup-data.md ## Article Content: # About Warmup Data Warmup data is used to optimize [page rendering](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-page-rendering.md) when your site's pages are rendered on both the server and the client, ensuring that costly data operations are done only once. Generally, the server executes data operations faster than the browser. With warmup data, you can pass the results of your server operations to your frontend code. During browser rendering, you can write code to check if those operations were already done and set to warmup data, and if the warmup data exists, use it in your code. This prevents the client from performing the same expensive operations again. Use the [Rendering API](https://dev.wix.com/docs/sdk/frontend-modules/window/rendering/introduction.md) to check whether your code is being run on the client or the server, and the [Warmup Data API](https://dev.wix.com/docs/sdk/frontend-modules/window/warmup-data/introduction.md) to set and get the warmup data. ## Supported IDEs You can optimize page rendering with `warmupData` using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) ## Browser rendering When working with warmup data it's important to keep in mind that there may be cases where browser rendering happens before server-side rendering. In such cases, you can't rely on the warmup data being set on the server, and need to execute those operations directly on the client-side. Note that you can only perform the following operations in the browser: - Access the browser's storage - Use 3rd-party analytics - Create dynamic event handlers - Retrieve information about the client's browser - Navigate to another page ## See also - [About page rendering](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/about-page-rendering.md) - [Optimize page rendering with warmup data](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/page-rendering/optimize-page-rendering-with-warmup-data.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the URL Structure of Site Pages ## Article: About the URL Structure of Site Pages ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/urls/about-the-url-structure-of-site-pages.md ## Article Content: # About the URL Structure of Site Pages Understanding the URL structure of Wix site pages is crucial for tasks such as routing, linking, SEO, and query parameter manipulation. ## Sample URLs The structure of the URLs differ between Wix premium sites and Wix free sites: - **Premium sites**: Use a custom domain, such as `domain.com`: `https://domain.com/animals/mammals/elephant?species=african-elephant`. - **Free sites**: Reflect the Wix editor as the domain followed by the site name, such as `user.wixstudio.io/zoo` for Wix Studio sites and `user.wixsite.com/zoo` for Wix Editor sites. - **Wix Studio**: `https://user.wixstudio.io/zoo/animals/mammals/elephant?species=african-elephant` - **Wix Editor**: `https://user.wixsite.com/zoo/animals/mammals/elephant?species=african-elephant` You can get the full site URL, as well as its parts, using the [Location API](https://dev.wix.com/docs/sdk/frontend-modules/location/introduction.md). ## Key parts of a URL | Term | Description | Example | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Protocol** | Standard part of the URL that indicates the protocol used to access the resource. | `https://` | | **Base URL** | Represents the root domain of your site. Premium sites use a custom domain, while free Wix sites include the editor followed by the site name. | Premium: `domain.com`
Free (Wix Studio): `user.wixstudio.io`
Free (Wix Editor): `user.wixsite.com` | | **Prefix** | Component used for dynamic paths and router pages. Prefixes are useful for making Dynamic Page URLs, or for determining which incoming requests the [router](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) handles. | `/animals` | | **Path** | The path typically follows the base URL or the prefix, if a prefix exists. For dynamic pages and router cases, paths are used for structure or to encode information to indicate what dynamic content to show. | `/mammals/elephant` | | **Query** | Query parameters are key-value pairs appended to the URL to pass additional data. | `?species=african-elephant` | ## See also - [Location API](https://dev.wix.com/docs/sdk/frontend-modules/location/introduction.md) - [Routers](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-backend/routers/about-routers.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Triggered Emails ## Article: About Triggered Emails ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/about-triggered-emails.md ## Article Content: # About Triggered Emails Triggered emails allow you to automatically send customized emails for specific site events. For example, you might trigger emails to send: - A coupon to customers on their birthday. - A satisfaction survey following an issued refund. - A "next steps" email after a form is submitted.
Tip: As an alternative to triggered emails, you can use Wix Automations to reach out to your visitors.
## Supported IDEs You can schedule recurring jobs using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) ## Monthly email marketing balance Every site has a quota of monthly emails available to send. You can check the current balance under **Monthly balance** in your site's [Email Marketing dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Email%20Marketing&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/shoutout/). Triggered emails count toward the [email marketing balance](https://support.wix.com/en/article/email-marketing-understanding-your-monthly-balance) but continue to send without interruption even once the quota is reached. You can also [upgrade your email marketing plan](https://www.wix.com/my-account/site-selector/?title=Select%20a%20Site&buttonText=Select%20Site&actionUrl=https%3A%2F%2Fwww.wix.com%2Fwix-emails-plans%3FmetaSiteId%3D%7B%7BmetaSiteId%7D%7D%26origin%3Dkb) for a larger sending quota. ## Statistics for triggered emails vs email marketing The [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) maintains [statistics](https://support.wix.com/en/article/viewing-your-email-marketing-statistics) related to the triggered emails you create. Triggered email statistics are based on all emails sent, including test emails you send to yourself. This is different from the statistics for email marketing campaigns in your [Email Marketing dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Open%20Email%20Marketing&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/{{metaSiteId}}/shoutout/), which are based on emails sent to unique contacts. Therefore, the statistics for triggered emails may seem inflated compared to email marketing statistics. ## Site collaborators [Site collaborators](https://support.wix.com/en/article/inviting-people-to-contribute-to-your-site#inviting-people-to-collaborate-on-your-site) can create and manage triggered emails. To do so, you must [give them](https://www.wix.com/my-account/site-selector/?buttonText=Manage%20this%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https://www.wix.com/dashboard/%7B%7BmetaSiteId%7D%7D/manage-website/authorization) both **Manage Triggered Emails** and **Manage Email Marketing Campaigns** [permissions](https://support.wix.com/en/article/roles-permissions-overview#roles-and-permissions). ## See also - [Create a triggered email](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/create-a-triggered-email.md) - [Tutorial | Send a triggered email to members](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-a-triggered-email-to-members.md) - [Tutorial | Send emails using the SendGrid npm package](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-emails-with-the-send-grid-npm-package.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Triggered Email ## Article: Create a Triggered Email ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/create-a-triggered-email.md ## Article Content: # Create a Triggered Email Set up a [triggered email](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/about-triggered-emails.md) to automatically send customized emails for specific events on your site. ## Step 1 | Navigate to the Triggered Emails dashboard page To access the Triggered Emails dashboard page, you can either: - Navigate to **Developer Tools** in the Code sidebar and click **Triggered Emails**. - Select **Developer Tools** from [your site’s dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) and select **Triggered Emails**. ## Step 2 | Create an email Use the editor to create and design a new email: 1. On your [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails), click **+ New Campaign** to create a new email, or click the **More Actions** ![More Actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0e583769d1305e30a5e8d6ca6ed59453.png) icon and select **Duplicate** to duplicate an existing triggered email. 2. Design your email in the editor. ## Step 3 (Optional) | Add variables to personalize text Add variables to personalize the text of your email. The variable is a placeholder that will be replaced by a value you define in the code. 1. With a text element selected in the editor, click **+ Add Variable**: ![Add Variable button in the triggered email editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fcaec601e7155d7ffa74cdcb27847d63.png) 2. Fill out the fields in the popup: - **Variable name**: Name the variable something meaningful so it's easy to identify later. For example, `newSubscriberName`. - **Fallback value**: A replacement text that displays here in case you're missing that specific piece of data. You can apply fallback values in the body of an email, but not in the subject line. For example, with the fallback value `there`, in a case where you don't have the new subscriber's name the email will fallback to "Hey there,". 3. Click **Add**. ## Step 4 | Test, save, or publish the email Once your email is designed you have the option at the top of the editor to preview, save, or publish the triggered email. ![Preview, Save as Draft, and Save & Continue options in the triggered email editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/de9b62255dd77d87f3eb37ee25727ff0.png) - Click **Preview & Test** to send yourself the email to see how it will appear in an inbox and to test any links you included in the email. - Click **Save as Draft** to save your email as a work-in-progress. Draft emails aren't sent when the triggering event occurs. - Click **Save & Continue**. If this is the first time ever you're creating a campaign, you'll be asked to fill out [sender details](#step-5--add-sender-details). That must be completed before publishing. ## Step 5 | Add sender details Sender details are required to send triggered emails. These details contain the information that appears as the sender's name and email address when an email from a site is received: - **Sender’s name**: This is the name that displays in the recipient's inbox. - **Reply-to email**: This is the email address that will populate as the send-to address in your reader's email when they click Reply.
Important information about the email address included in reply-to email: - Emails sent with a reply-to email with a public domain, such as `@gmail.com` or `@yahoo.com`, tend to land in the spam or promotions category. It's better to use an email address with an authenticated domain, such as `you@yourbusiness.com`. - If you use a public domain address, Wix redirects your email through our own authenticated address `@wixemails.com` or, for free users, `@wixsitemail.com`. When someone replies to your email, their new email will automatically populate the send field with the reply-to email. - You may be asked to confirm your reply-to email. You'll receive an email in your inbox with those instructions.
Click **Save** to continue. ## Step 6 | Get the code snippet Before redirecting back to the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) you have the opportunity to make changes to the email ID and the audience who will receive the email: ![Email ID and code snippet window showing options to email new contacts or site members](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9c5d6d3da3f03171968688308d8cc1be.png) 1. Update the unique **Email ID** to one that's more friendly to work with. For example, `newsletter_signup`. This email ID is used in the code snippet. 2. Click **Email new contacts** or **Email site members** to display the appropriate snippet. 3. Copy the code snippet now or access it later from the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails). 4. Click **Save**. ## Step 7 | Add the code snippet to an event on your site Take the code snippet you copied from the previous window or hover over a triggered email in the [Triggered Emails dashboard page](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fdeveloper-tools/triggered-emails) and click the **View code snippet** ![View code snippet icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9463eeb00bd681a55c5b3e55b3ea8a75.png) icon and add the snippet to your code. ## See also - [About triggered emails](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/triggered-emails/about-triggered-emails.md) - [Tutorial | Send a triggered email to members](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-a-triggered-email-to-members.md) - [Tutorial | Send emails using the SendGrid npm package](https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/members-contacts/tutorial-send-emails-with-the-send-grid-npm-package.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Mobile Sites with Code ## Article: About Mobile Sites with Code ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/mobile/about-mobile-sites-with-code.md ## Article Content: # About Mobile Sites with Code When you build a site, Wix automatically creates a mobile version. Wix resizes your site's content to fit mobile screens and hides any elements from the desktop editor that aren't optimized for mobile. You can customize the mobile version of your site by: - Writing code to change the site's behavior when a site visitor views it on a mobile device or tablet. For example, you can show text on the desktop when hovering over an element. But this functionality isn't available on a mobile device. Instead, you can show the text when tapping. - Managing elements in the [Mobile Editor](https://support.wix.com/en/article/wix-editor-getting-started-with-the-mobile-editor) (Wix Editor) or by using [breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) (Wix Studio). For example, you can hide a button when a visitor opens the site on a mobile device or tablet. This doesn't require coding knowledge. This article only discusses how to manage elements with code. ## Supported IDEs You can write code for mobile using: - The [editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/code-editor/about-the-code-editor.md) (Wix Studio and Wix Editor) - The [Wix IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/wix-ide/about-the-wix-ide.md) (Wix Studio) - Your [local IDE](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/ides/git-integration/about-git-integration-with-wix-cli.md) (Wix Studio and Wix Editor) ## Managing site behavior on different devices with code To write code that only runs on mobile devices, you first need to check what device the code is running on. You do this by wrapping the mobile-only code in a JavaScript conditional statement with the [Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md). Your code will look something like this: ```js import { window } from "@wix/site-window"; (async () => { if ((await window.formFactor()) === "Mobile") { // code that will only run on mobile } })(); ``` ## Considerations Consider the following when coding for mobile sites: - You might expect that code written in the code editor while in mobile view would only run when your site is viewed on a mobile device. However, the code actually runs regardless of the type of device on which your site is being viewed. Be sure this code doesn’t display anything unnecessary in desktop view. - Some event handlers, such as `onMouseIn()` or `onMouseOut()` don't work on mobile. You should replace these handlers with `onClick()`. For example: Code for hovering an image in desktop view: ```js import { window } from "@wix/site-window"; $w.onReady(function () { $w("#image").onMouseIn(async (event) => { await $w("#hiddenText").show("fade"); }); $w("#image").onMouseOut(async (event) => { await $w("#hiddenText").hide("fade"); }); }); //... ``` Code for tapping the same image in mobile view: ```js //... $w.onReady(function () { $w("#image").onClick(async (event) => { if ( (await window.formFactor()) === "Mobile" || (await window.formFactor()) === "Tablet" ) { if ($w("#hiddenText").hidden) { $w("#hiddenText").show("fade"); } else { $w("#hiddenText").hide("fade"); } } }); }); ``` ## See also - [Mobile Editor](https://support.wix.com/en/article/wix-editor-getting-started-with-the-mobile-editor) - [Breakpoints](https://support.wix.com/en/article/studio-editor-designing-across-breakpoints) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Custom Code ## Article: About Custom Code ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-frontend/custom-code/about-custom-code.md ## Article Content: # About Custom Code Custom code lets you add HTML, JavaScript, and CSS snippets to the head or body of a site's pages. You can use it to integrate external services, customize site behavior, and add functionality that isn't available through the editor's built-in tools. You add custom code from the site dashboard under **Settings > Custom Code**. Custom code can be used in all Wix editors. For Wix Harmony sites, which don't support site code, custom code is one of the primary ways to extend a site's functionality beyond what the editor provides. > **Note:** To add custom code, the site must be [published](https://support.wix.com/en/article/wix-editor-publishing-your-site) and have a [connected domain](https://support.wix.com/en/article/connecting-a-domain-to-your-site-195136). ## How it works When you add a custom code snippet, you configure the following: - **Placement**: Where the snippet is injected in the page's HTML. Options include the `` tag, the start of the `` tag, or the end of the `` tag. - **Scope**: Whether the snippet loads on all pages or only on specific pages. - **Loading behavior**: Whether the snippet loads once per visit or on every page the visitor opens. Custom code snippets don't appear as elements in the editor. They run behind the scenes when the page loads, but their effects, such as applied styles, injected UI elements, or data fetched from external services, are visible to site visitors on the published site. ## Use cases You can use custom code to integrate 3rd-party services, style elements beyond what the editor offers, add interactive JavaScript, and improve SEO with structured data. ### Integrate 3rd-party services Many external services require a code snippet to be added to a website. Custom code gives you a place to paste these snippets so they load alongside the site. Common examples include: - **Site verification**: Search engines like Google and Bing require a `` tag in the site's `` to verify site ownership. - **Live chat widgets**: Customer support platforms provide a script that displays a chat widget on the site. - **Cookie consent banners**: Privacy compliance tools provide a snippet that displays a consent banner to meet GDPR or CCPA requirements. ### Add custom CSS You can inject a ` # Wix’s Development Frameworks Whether you're a seasoned developer or just dipping your toes into the world of coding, Wix offers an arsenal of developer-friendly tools and technologies to drive your [app-building journey](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-apps.md#your-app-building-journey). These tools and technologies are integrated within 3 frameworks, each providing a comprehensive development model. This article guides you through the process of choosing the development framework that’s right for you. The main choice is who manages your app's hosting and deployment: * **Wix-managed**: Wix hosts and deploys your app on the Wix cloud, with no additional setup or cost. Build it code-first with the [Wix CLI](#wix-cli), or visually with [Wix Blocks](#wix-blocks). * **Self-managed**: Use your own tech stack and [host your app yourself](#self-managed). [](https://manage.wix.com/account/custom-apps) ## Wix CLI Use Wix CLI to code and deploy with Wix’s React/Node.js stack. Get the standard JavaScript developer experience, code on your local machine with your preferred IDE, and collaborate with your team using git. Your code is deployed and hosted on the Wix cloud, with no additional costs or setup. [Learn more about Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) Here's what creating an app with Wix CLI looks like: ![cli](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ec781a4cfa271092a735cb5cd933804d.gif) ## Self-managed With the self-managed framework, you use your own tech stack and handle deployment and hosting yourself. When setting up your application in your app's dashboard, simply provide URLs to your deployment assets, such as iframes, webhooks, and JavaScript bundles. [Learn more about self-managed apps](https://dev.wix.com/docs/build-apps/develop-your-app/develop-a-self-managed-app/about-self-managed-apps.md) Here's an example of registering a self-managed iframe in your app's dashboard: ![dev-center](https://wixmp-833713b177cebf373f611808.wixmp.com/images/55408761d7d5d5d13075f262fe59a75b.png) ## Wix Blocks If you prefer a visual, low-code approach, you can use Wix Blocks to design, code, and deploy on Wix’s native app editor. Build site and dashboard components using Wix’s drag-and-drop editor, with its powerful layout and design tools, and code your business logic using [Velo](https://dev.wix.com/docs/develop-websites/articles/get-started/about-developing-websites.md), Wix’s native coding solution. Your code is deployed and hosted on the Wix cloud, with no additional costs or setup. [Learn more about Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) Here's what Wix Blocks looks like: ![blocks](https://wixmp-833713b177cebf373f611808.wixmp.com/images/964d8ea6d8a139d48b9364d79b6d86fd.png) ## Choosing a framework Start by identifying the [extensions](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) your app needs, then check which frameworks support each one in the [extension catalog](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md#extension-catalog). With your required extensions in mind, consider: - **Hosting and deployment**: Wix CLI and Wix Blocks deploy automatically to the Wix cloud, with no additional setup or cost. Self-managed gives you full control over hosting and deployment on your own infrastructure, in exchange for managing it yourself. - **Tech stack and tooling**: Wix CLI provides a standard TypeScript workflow in your preferred IDE. Self-managed lets you use any stack and tools you already work with. Wix Blocks uses Wix’s native online editor and coding solution. - **Collaboration and version control**: Wix CLI and self-managed both integrate with git and standard CI/CD practices. ## Combine frameworks A Wix app’s functionality is based on a set of [extensions](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) that are added to a site, for example, a dashboard page, a site widget or specific backend logic. You can build each of these extensions using one or more of Wix’s development frameworks. It’s important to understand that no matter which framework you start with, you can always continue developing your app using any of the other frameworks. Your app can contain multiple extensions, each built with a different framework. Because of this, Wix-managed and self-managed describe how an individual extension is built and hosted, not necessarily a whole app. In fact, there are several use cases that require multiple frameworks in the same app. Learn more about the [frameworks you can use to build each type of extension](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md). ## Summary So which framework should you choose? Basically, it’s a question of which tech stack and tools you prefer to work with. Here’s a summary of the main features of each development framework: | **Wix CLI** | **Self-managed** | **Wix Blocks** | |---|---|---| |
  • Standard TypeScript full-stack developer experience.
  • Your preferred local IDE.
  • App scaffolding, live preview, and deployment flows.
  • Collaboration and version control on git.
  • Hosted on the Wix cloud.
|
  • Your own tech stack and tools.
  • Limited access to Wix developer utilities.
  • You manage hosting on the platform of your choice.
|
  • Drag-and-drop UI editor.
  • Native Wix coding solution in an online IDE.
  • No-code and low-code features.
  • Frontend and backend coding, databases, and deployment flows.
  • Hosted on the Wix cloud.
| --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: How Apps Extend Wix ## Article: How Apps Extend Wix ## Article Link: https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md ## Article Content: # How Apps Extend Wix Your app can add functionality to almost every part of the [Wix ecosystem](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md). It can display a widget on a Wix user's site, add a page to a site's dashboard, inject custom logic into a site's backend service, and much more. Each specific type of functionality that an app can provide is called an extension. See the full list of available extensions below. An app can contain multiple extensions that work together to expose its functionality across different user interfaces and backend services. For example, Wix's [FAQ app](https://www.wix.com/app-market/wix-faq) displays a customizable FAQ widget on a site, and also has a dashboard page where the Wix user can create and manage their FAQ categories, questions, and answers. Most extensions are built using one of Wix's [development frameworks](https://dev.wix.com/docs/build-apps/get-started/overview/wix-s-development-frameworks.md). However, some extensions only require configuration in the [app dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md) with no coding involved. You can extend Wix's [frontend interfaces](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md#frontend-interfaces) as well as various [backend capabilities](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md#backend-capabilities). Frontend extensions include: - [Site extensions](#site-extensions): Components that are added to a site. - [Dashboard extensions](#dashboard-extensions): Components that are added to the dashboard. - [Editor extensions](#editor-extensions): Components that are added to the editor. Backend extensions include: - [Service plugins](#service-plugins): Business logic that's injected into a site's backend services. - [Schema plugins](#schema-plugins): Fields that are added to the database schemas of Wix business solutions. - [Notifications](#notifications): Notifications sent to Wix users. - [Automations](#automations): Triggers, actions, and pre-installed automations that automate business processes. - [APIs](#apis): Backend methods that can be called from frontend code. - [Events](#events): Code that runs when specific events occur on a site. ## App compatibility App compatibility varies depending on the type of site a Wix user has. Sites built with the drag-and-drop editors, Wix Editor and Wix Studio, can use any type of app. However, sites built with Wix Vibe and Headless projects can only use apps that only have dashboard extensions. ## Extension catalog The following tables list all available extensions: ### Frontend extensions #### Site extensions | Extension | What you create | How you build it | | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | [Site page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md) | A page that's added to a site, composed of selected widgets. | Configuration in the app dashboard | | [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) | A draggable component that can be placed anywhere on a site. | One of the following:
  • Wix Blocks
  • Wix CLI custom element
  • Self-managed custom element
| | [Site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) | A site component that extends an app created by Wix. It can be placed inside designated areas (called slots) within the hosting app's site widget. | One of the following:
  • Wix Blocks
  • Wix CLI custom element
  • Self-managed custom element
| | [Embedded script](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) | A script that's injected into a site's HTML code. | One of the following:
  • Wix CLI
  • Configuration in the app dashboard
| #### Dashboard extensions | Extension | What you create | How you build it | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | | [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) | A page that's added to a site's dashboard. | One of the following:
  • Wix Blocks
  • Wix CLI
  • Self-managed iframe
| | [External link](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/external-links/about-external-link-extensions.md) | A link in the dashboard's [app management page](https://support.wix.com/en/article/accessing-your-apps#accessing-your-apps-from-your-sites-dashboard-1), pointing to your external site. | Configuration in the app dashboard | | [Dashboard plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/about-dashboard-plugin-extensions.md) | A dashboard component that [extends an app created by Wix](https://dev.wix.com/docs/build-apps/get-started/overview/integrating-with-wix-s-business-solutions.md). It's placed inside a designated area (called a slot) in the hosting app's dashboard page. | One of the following:
  • Wix CLI
  • Self-managed iframe
| | [Dashboard menu plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/dashboard-menu-plugins/dashboard-menu-plugin-extensions.md) | An item that's added to a menu in a dashboard page of an app created by Wix. The menu item can trigger various actions. | One of the following:
  • Wix CLI
  • Configuration in the app dashboard
| | [Dashboard modal](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-modals/about-dashboard-modals.md) | A modal to display in the dashboard. | One of the following:
  • Wix CLI
  • Self-managed iframe
| #### Editor extensions | Extension | What you create | How you build it | | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------------ | | [Editor Add-on](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/editor-extensions/about-editor-add-on-extensions.md) | A tool that adds functionality to the Wix editors. | Self-managed iframe | ### Backend extensions #### Data collections | Extension | What you create | How you build it | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | ---------------------------------- | | [Data collections](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/data-collections/about-data-collections-extensions.md) | CMS data collections that are automatically created when your app is installed on a site. | [Configuration in the app dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/data-collections/add-a-data-collections-extension-in-the-app-dashboard.md) | #### Service plugins | Extension | What you create | How you build it | | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [eCommerce additional fees service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/additional-fees/additional-fees-service-plugin/introduction.md) | Business logic for calculating additional fees for a site visitor's cart and checkout. | CLI or self-managed service plugin | | [eCommerce shipping rates service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/shipping-rates/shipping-rates-integration-service-plugin/introduction.md) | Business logic for calculating shipping rates for a site visitor's cart and checkout. | CLI or self-managed service plugin | | [eCommerce custom discount trigger service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/custom-discount-triggers-integration-service-plugin/introduction.md) | Business logic for applying discount rules to a site visitor's cart and checkout. | CLI or self-managed service plugin | | [eCommerce payment settings service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/payments/payment-settings/payment-settings-integration-service-plugin/introduction.md) | Business logic to determine whether to apply 3D Secure validation during a merchant's payment process. | CLI or self-managed service plugin | | [eCommerce validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md) | Business logic for validating a site visitor's cart and checkout. | CLI or self-managed service plugin | | [eCommerce recommendations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/other-services/recommendations/recommendations-service-plugin/introduction.md) | Business logic for providing advanced recommendation algorithms, helping Wix users deliver personalized suggestions that enhance user engagement and overall experience. | Self-managed service plugin | | [eCommerce catalog service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/catalog-service-plugin/introduction.md) | Business logic for becoming a Wix catalog provider and integrating any external repository of sellable items with the Wix eCommerce platform. | CLI or self-managed service plugin | | [eCommerce gift vouchers service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/payments/gift-cards/gift-cards-service-plugin/introduction.md) | Business logic for integrating with Wix as a gift card service provider. | CLI or self-managed service plugin | | [eCommerce inventory service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/inventory-service-plugin/introduction.md) | Business logic for integrating with a unified inventory system, enabling decrementing and incrementing item availability based on order flows. | REST: Self-managed service plugin | | [Stores product restrictions service plugin](https://dev.wix.com/docs/api-reference/business-solutions/stores/service-plugins/product-restrictions-v3/introduction.md) | Business logic for implementing editing restrictions on product fields for 3rd-party fulfillment apps such as dropshipping and print-on-demand. | REST: Self-managed service plugin | | [Marketing keyword suggestions service plugin](https://dev.wix.com/docs/api-reference/business-management/marketing/seo/seo-keywords-suggestions-service-plugin/introduction.md) | Business logic for providing Wix users with keyword suggestions through the site dashboard. | CLI or self-managed service plugin | | [App Billing custom charges service plugin](https://dev.wix.com/docs/api-reference/app-management/app-billing/custom-charges-service-plugin/introduction.md) | Business logic for charging Wix users for using your app. | Self-managed service plugin | | [Form submission service plugin](https://dev.wix.com/docs/api-reference/crm/forms/service-plugins/form-submissions-service-plugin/introduction.md) | Business logic for adding additional form validations. | CLI or self-managed service plugin | | [Bookings pricing integration service plugin](https://dev.wix.com/docs/api-reference/business-solutions/bookings/pricing/pricing-integration-service-plugin/introduction.md) | Business logic for providing custom pricing calculations for your merchant's bookings. | REST: Self-managed service plugin | | [Data external database service plugin](https://dev.wix.com/docs/api-reference/business-solutions/cms/external-databases/external-database-service-plugin/introduction.md) | Business logic for providing custom pricing calculations for your merchant's bookings. | Self-managed service plugin | | [Automations action provider service plugin](https://dev.wix.com/docs/api-reference/business-management/automations/actions/action-provider-service-plugin/introduction.md) | Business logic for implementing custom automated actions. | REST: Self-managed service plugin | | [Payments provider service plugin](https://dev.wix.com/docs/api-reference/business-management/payments/service-plugins/payment-service-provider-service-plugin/introduction.md) | Business logic for integrating payments with Wix. | REST: Self-managed service plugin | | [Payments tax calculation service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/tax/tax-calculation-integration-service-plugin/introduction.md) | Business logic for calculating taxes. | CLI or self-managed service plugin | | [Payments tax groups service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/tax/tax-groups-integration-service-plugin/introduction.md) | Business logic for managing default tax groups to categorize products from your app's catalog based on distinct tax treatments. | REST: Self-managed service plugin | Learn more [about service plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/service-plugins/about-service-plugin-extensions.md). #### Schema plugins | Extension | What you create | How you build it | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | ---------------------------------- | | [Booking schema plugin](https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/booking-object.md) | Fields that are added to the Wix Booking object. | Configuration in the app dashboard | | [Booking service schema plugin](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/service-object.md) | Fields that are added to the Wix Booking Service object. | Configuration in the app dashboard | | [Checkout](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/checkout/checkout/checkout-object.md) & [Order](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-object.md) schema plugin | Fields that are added to the Checkout and Order objects. | Configuration in the app dashboard | | [eCommerce catalog product schema plugin](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/product-object.md) | Fields that are added to the Wix Stores Product object. | Configuration in the app dashboard | | [eCommerce tips settings schema plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/other-services/tips/tip-settings/tip-settings-object.md) | Fields that are added to the Tips Settings object. | Configuration in the app dashboard | Learn more [about schema plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md). #### Notifications | Extension | What you create | How you build it | | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------- | | Notification | Notifications that Wix users receive on their site dashboard and in the Wix Owner mobile app. | Self-managed notification logic | | Notification topic | Topics that you can use to group notifications when they're displayed to Wix users. | Configuration in the app dashboard | Learn more [about notification extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/notifications/about-notification-extensions.md). #### Automations | Extension | What you create | How you build it | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | | [Trigger](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/triggers/about-triggers.md) | An event that causes an automation to run, such as a visitor action or a Wix user action. | Configuration in the app dashboard | | [Action](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/actions/about-actions.md) | An automation's response when triggered, carrying out processes such as sending an email or updating data. | Configuration in the app dashboard | | [Pre-installed automation](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/pre-installed-automations/about-pre-installed-automations.md) | A full automation combining a trigger and 1 or more actions that's added to a user's site when they install your app. | Configuration in the app dashboard | Learn more [about automations](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/about-automations.md). #### APIs | Extension | What you create | How you build it | | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------- | | [Web method](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/web-methods/about-web-method-extensions.md) | A method defined in your app's backend that can be called from frontend code. Wix handles the communication. | CLI | | [HTTP function](https://dev.wix.com/docs/wix-cli/guides/development/http-endpoints/about-http-endpoints.md) | An HTTP method in your app's backend that can be called from frontend code. | CLI | #### Events | Extension | What you create | How you build it | | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------- | | [Event](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/events/about-event-extensions.md) | Code that runs when specific events occur on your app or a user's site, such as a booking confirmation. | CLI | ## See also - [Map your app's functionality to Wix's extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/map-your-app-s-functionality-to-wix-s-extensions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrating with Wix's Business Solutions ## Article: Integrating with Wix's Business Solutions ## Article Link: https://dev.wix.com/docs/build-apps/get-started/overview/integrating-with-wix-s-business-solutions.md ## Article Content: # Integrating with Wix’s Business Solutions Wix offers a range of powerful [business solutions](https://support.wix.com/en/business-solutions-apps), enabling Wix users to sell products and services, run events, write blogs, manage restaurants, and more. These business solutions are designed to integrate seamlessly with Wix websites. Users add Wix business solutions to their sites by installing [apps created by Wix](https://www.wix.com/app-market/collection/made-by-wix). ## How to integrate Integrate your app’s functionality into one of Wix’s native business solutions to tap into Wix’s vast user base, gain exposure, and generate revenue. Whether your app fulfills orders with dropshipping, boosts a blog writer's audience, or helps a restaurant manage its staff, there's an opportunity for you to offer Wix users the extra features they need. Your app can integrate with Wix’s business solutions in the following ways: * **Plugins**: Inject your own functionality directly into a business solution’s user interfaces and backend capabilities. * **APIs**: Connect your services to Wix sites by gaining access to a business solution’s data and functionality. ## Plugins With plugins, you can inject your own functionality directly into the user interfaces and backend capabilities of Wix’s business solutions. The following image shows an example of the [Wix Reviews](https://support.wix.com/en/article/wix-stores-adding-and-setting-up-wix-reviews) site plugin, which adds a product rating component inside the Wix Stores product page: ![site-plugin](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ba3d9720e4f1147b49de6fde6780522f.png) ### Frontend plugins Create plugins for the site and dashboard interfaces of apps created by Wix: * [Site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md): Components that users can place inside designated areas (called slots) within the site pages of an app created by Wix, such as the Stores product and checkout pages. * Dashboard plugins: * [Dashboard plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/about-dashboard-plugin-extensions.md): Components that fit into designated areas (called slots) within a dashboard page of an app created by Wix. * [Dashboard menu plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/dashboard-menu-plugins/dashboard-menu-plugin-extensions.md): Items added to the menu in the dashboard page of an app created by Wix. ### Backend plugins Create plugins for the backend capabilities of apps created by Wix: * [Service plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/service-plugins/about-service-plugin-extensions.md): Business logic that’s injected into a site’s backend services. * [Schema plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md): Fields that are added to the database schema of a Wix business solution. ## APIs Take advantage of the diverse business solutions that Wix offers to enable communication and integration between your app and Wix. Wix offers multiple [API technologies](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md), including REST, JavaScript SDK, GraphQL, and Velo with Blocks. Each API technology offers a similar set of APIs. The main business solution APIs include: * [Wix eCommerce](https://www.wix.com/ecommerce): Manage the cart, checkout, and order phases of an online store experience. Explore [eCommerce APIs](https://dev.wix.com/docs/rest/business-solutions/e-commerce/introduction.md). * [Wix Stores](https://www.wix.com/app-market/wix-stores): Handle product catalogs, inventories, and collections. Explore [Stores APIs](https://dev.wix.com/docs/rest/business-solutions/stores/about-wix-stores.md). * [Wix Bookings](https://www.wix.com/app-market/bookings): Accept and manage bookings for services. Explore [Bookings APIs](https://dev.wix.com/docs/rest/business-solutions/bookings/about-wix-bookings.md). * [Wix Pricing Plans](https://www.wix.com/app-market/paid-plans): Access and manage customized paid membership plans for content and services. Explore [Pricing Plans APIs](https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/introduction.md). * [Wix Events](https://www.wix.com/app-market/web-solution/events?searchLocation=home): Create events, sell tickets, and collect RSVPs. Explore [Events APIs](https://dev.wix.com/docs/rest/business-solutions/events/introduction.md). * [Wix Blog](https://www.wix.com/app-market/wix-blog): Access and manage blogs, including posts, draft posts, categories, tags, and statistics. Explore [Blog APIs](https://dev.wix.com/docs/rest/business-solutions/blog/introduction.md). * [Wix Groups](https://www.wix.com/app-market/wix-groups): Create and manage groups for posting updates, sharing media, starting discussions, and more. Explore [Groups APIs](https://dev.wix.com/docs/rest/crm/community/groups/introduction.md). * [Wix Forum (deprecated)](https://support.wix.com/en/article/wix-forum-about-wix-forum): Access and manage a business's member forum, including posts and categories. Explore [Forum APIs](https://dev.wix.com/docs/rest/business-solutions/forum/introduction.md). * [Wix Loyalty Program](https://support.wix.com/en/article/wix-loyalty-program-an-overview): Create and manage loyalty programs to increase customer retention. Explore [Loyalty APIs](https://dev.wix.com/docs/rest/crm/loyalty-program/introduction.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Exposing Apps Publicly and Privately ## Article: App Exposure: Public or Private? ## Article Link: https://dev.wix.com/docs/build-apps/get-started/overview/exposing-apps-publicly-and-privately.md ## Article Content: # Exposing Apps Publicly and Privately When you build an app, you can decide whether you want that app to be: - **Public**: Available for general use, whether listed in the Wix App Market or distributed on your own. - **Market listed**: Listed in the Wix App Market. - **Unlisted**: Available for general use, not listed in the Wix App Market. - **Private**. Only available on your own sites or your enterprise sites. ## Public apps Public apps are applications that are available to all Wix users to install. They might be listed in the [Wix App Market](https://www.wix.com/app-market), or they might be available via [install link](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/share-an-app-install-link.md). ### Market listed apps Market listed apps are applications that are listed in the [Wix App Market](https://www.wix.com/app-market). In the Wix App Market, your apps are visible to millions of Wix users and can be found through search engines like Google. You can choose to offer your apps for free or sell them. You can also decide on the size of your app’s audience by limiting it to certain countries or languages. For example, you could build a shipping app just for Indian users. Learn more about [listing apps in the App Market](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/app-market-guidelines.md). ### Unlisted apps Unlisted apps are applications that you make available for general use but are not listed in the Wix App Market. Unlisted apps can be installed through: - The app dashboard (if you are an owner or contributor to the site), from the **App Distribution** page. - A [shareable URL](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/share-your-app-with-an-install-link.md), which you can create in [Custom Apps page](https://manage.wix.com/account/custom-apps), the **App Distribution** page of your app's dashboard for Wix CLI and self-managed apps, or directly in Wix Blocks through **App** > **Share App**. ## Private apps Private apps are applications that are used on your own sites, on your enterprise sites, or for other collaborative projects. You can install these apps on as many Wix sites as you want, as long as you are an owner or contributor to the site. Enjoy the benefits of reusing the app's user-interface, code, collections, and dashboard pages. Private apps can be installed through: - The editor (if you’re the app’s creator). - The Wix Studio dashboard. - A [shareable URL](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/share-an-app-install-link.md), which you can create in [Custom Apps page](https://manage.wix.com/account/custom-apps), the **App Distribution** page of your app's dashboard for Wix CLI and self-managed apps, or directly in Wix Blocks, through **App** > **Share App**. ### Private apps for enterprise use With a Wix enterprise account, you can build and reuse apps among all of your organization’s accounts and websites. Private apps built by enterprises can be exposed to all enterprise users through the Wix App Market using a ‘private collection’ that can appear in the Wix App Market sidebar. ## See also - [About the App Dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md) - [The Wix App Market](https://www.wix.com/app-market) - [Publishing Your Wix Blocks App to the App Market](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/publish-blocks-apps-to-the-app-market/publish-a-blocks-app-to-the-app-market.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Wix Harmony and Apps ## Article: Wix Harmony and Apps ## Article Link: https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md ## Article Content: # About Wix Harmony and Apps Wix Harmony is Wix's new AI-powered site editor, built on a technical architecture that's different from previous editors. With Harmony, the way apps and extensions work across Wix is evolving. This overview explains app compatibility in Harmony, what's changing, and your options for supporting users across all Wix editors and frameworks. > **Note:** For information about site development, see [About Wix Harmony](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-wix-harmony.md). For Blocks-specific information, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md). ## App compatibility at a glance Apps can be installed in Harmony, though the UI and App Market experience differs from other editors. Due to architectural and technology updates, some app types are only available in certain editors. | Extension type | Wix Harmony | Wix Editor & Wix Studio | | --------------------------------- | :---------: | :---------------------: | | Dashboard extensions | ✓ | ✓ | | Backend extensions | ✓ | ✓ | | CLI site extensions | ✓ | ✓ | | Self-managed site extensions | ✓ | ✓ | | Blocks site widgets and plugins | — | ✓ | | Legacy custom elements | — | ✓ | | Legacy iFrame widgets and pages | Partial | ✓ | | Editor React Components | ✓ | — | ## What this means for existing apps Your app's availability in Harmony depends on the types of extensions it includes: - **Supported extensions:** Your app works in Harmony with no changes required. - **Unsupported extensions:** Your app works on Wix Editor and Wix Studio, but can't be installed by Harmony users. To reach Harmony users, rebuild unsupported extensions using the [Wix CLI for Apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli-for-apps.md) or [self-managing](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting.md). - **Partial support (iFrames):** If your app uses legacy iFrame widgets or pages, test your app thoroughly in Harmony. If you encounter issues, [reach out to us](https://dev.wix.com/docs/build-apps/manage-your-app/contact-us/submit-support-requests.md) for help. ## Recommendations for new apps When building a new app, use frameworks supported in Harmony: - **Wix CLI:** Build app extensions using your preferred IDE, with local development, testing tools, and full control over your code. Learn more in the [Wix CLI documentation](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli-for-apps.md). - **Self-managed:** Use your own infrastructure for backend, site, or dashboard extensions. Learn more about [self-managed extensions](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/about-self-hosted-app-extensions.md). ## Test your app We recommend testing your app in Harmony to ensure it works as expected. Create a Harmony development site to test your app's functionality and user experience. To learn more, see [Test Your App on a Free Premium Site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app-on-a-premium-site.md). ## FAQ ### Migration and compatibility
Can Blocks apps be migrated to the CLI or site widgets? Currently, there is no automatic migration path to convert Blocks apps directly to the new CLI or site widget tools. To move to the new architecture, you need to build a new extension. We've created a [step-by-step guide](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/migrate-to-a-new-site-widget-or-plugin-extension.md) on how to rebuild and update your app using the new tools. We're also exploring solutions to streamline this process in the future.
What happens to existing Blocks apps when customers move from Classic/Studio to Harmony? Currently, there is no migration tool from Classic/Studio to Wix Harmony.
Is there a way to move existing users from a Blocks app to a new implementation without losing reviews or installs, or without supporting 2 app versions? You can maintain your existing reviews and install base by adding your new implementation as an additional extension within your existing app, rather than creating a new app. We will be releasing a guide to assist you in migrating your active users from the old extension to the new one.
### Business impact and App Market visibility
Why are Blocks apps not discoverable or installable for Harmony users? Wix Harmony is built on a completely new technology that doesn't support Blocks apps, which means they aren't available for installation.
How can developers quickly mitigate lost customer acquisition and revenue due to Harmony not supporting Blocks apps? The current path to compatibility with Harmony is to build site widgets using Custom Elements, managed via the [Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli-for-apps.md) or [self-managing](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting.md). We encourage you to adopt this approach now to regain access to that user base. Additionally, we're working on a comprehensive React-based infrastructure that will provide even more robust options for app development in the near future.
What immediate options exist so current and new users can still access these apps? Wix Editor and Wix Studio users still have full access to your apps. If you need to support Wix Harmony sites now, you can start building [self-managed](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting.md) or [CLI](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli-for-apps.md) site widgets.
### Long-term strategy and timelines
What are the timelines for deprecations and continued support for the Classic and Studio editors? We aren't breaking any existing capabilities. If at any time we move forward with deprecation, we will make sure there is a clear and concrete plan that addresses all questions and concerns.
## Get support If you encounter issues or have questions, feel free to [reach out to us](https://dev.wix.com/docs/build-apps/manage-your-app/contact-us/submit-support-requests.md) or ask in our [Discord community](https://discord.gg/wixstudio). ## See also - [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md) - [About Wix Harmony](https://dev.wix.com/docs/develop-websites-sdk/get-started/overview/about-wix-harmony.md) - [About Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) - [About Development Frameworks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/about-development-frameworks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Glossary ## Article: Glossary ## Article Link: https://dev.wix.com/docs/build-apps/get-started/overview/glossary.md ## Article Content: # Developers' Glossary ## **Access type** Authorization profile used to interact with Wix. Every actor who interacts with Wix, whether they log into Wix, visit a Wix site, or make API calls, must authorize their calls using an access type. Access types include visitor, member, user, app and API key. ## **App ID** Unique ID that identifies your app in Wix. Find it on your app’s main page and OAuth page in the app dashboard. ## **API key** Account-level access token for REST and SDK API calls, that can be used for account-level APIs as well as site-level APIs. Account-level access provides access only to the specific sites that are managed by the relevant account. Only available to select apps after App Market approval. ## **App instance** Properties that describe your app's installation on a specific site. These properties include the app instance ID, user ID, premium flag and more. ## **App instance ID** Unique ID that identifies your app's installation on a specific site. Use this ID to identify specific sites in your app's code. ## **Custom app** App that is available only on your own sites, on your clients' sites, or for other collaborative projects. Custom apps are not listed in the Wix App Market. ## **Dashboard** Interface where users can manage their site and installed apps. Dashboards aren’t exposed to site visitors. ## **Development site** Free, pre-made premium site available to all third-party app developers for testing purposes. ## **Editor** Interface where site builders design their site’s visual components. Wix currently offers 2 different editors: [Wix Editor](https://support.wix.com/en/using-the-wix-editor) and [Wix Studio](https://support.wix.com/en/wix-studio). ## **Extension** Functionality that an app adds to a site, for example, a dashboard page, a site widget or specific backend logic. ## **Extension ID** Unique ID that identifies your extension in Wix. ## **Identity** Unique identifiers Wix uses to identify actors. Each identity has their own ID and access type. Identities include visitor, member, user, app and API key. ## **Permission scope** Sets of permissions that you can request from Wix users when they install your app. These permissions enable you to access and manage relevant site data. You can configure the permissions scopes for your app in the app dashboard. ## **Plugin** Extension that adds functionality to an app created by Wix, with either a visual component or backend logic. ## **Public key** Encryption key used to verify that data payloads were sent by Wix. You’ll need this to read webhook and SPI call data. You can find your app's public key on the Webhooks page in the app dashboard. ## **Query builder** Function that builds a query to retrieve a list of items. ## **Secret key** Encryption key used to create your app’s signature. You’ll need this to sign requests to Wix APIs, and Wix uses it when sending data to your app. You can find it on the OAuth page in the app dashboard. ## **Service plugin (formerly SPI)** APIs designed to be implemented by Wix users or by third parties, enabling you to customize Wix's business logic within a specific business solution. ## **Slot** UI placeholder within a site or dashboard page of an app created by Wix, where users can place a [plugin](#plugin). ## **User ID** Unique ID that identifies a [Wix user](#wix-user). ## **Wix app** Any app that extends the Wix ecosystem, including apps made by Wix, apps made by third parties and apps made by site builders. A third-party app can be public (available on the Wix App Market) or private (see [custom app](#custom-app)). ## **Wix Blocks** [Editor](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) for building responsive Wix apps. ## **Wix business solution** Comprehensive app made by Wix for managing a specific type of business, such as Wix Stores and Wix Bookings. ## **Wix CLI** [Command-line](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) interface tool that streamlines building Wix apps with minimal setup and configuration. ## **Wix Design System** [Collection](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md) of reusable React components and clear standards for designing Wix apps with the Wix look and feel. ## **Wix Patterns** [Collection](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md) of Wix Design System's reusable React components organized into fully-functional design and behavior presets for common types of functionality. ## **Wix user** Anyone who creates an account on wix.com. Users include site owners, who create a site, and site collaborators, who are invited by site owners to help manage the site and business. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Quick Starts ## Article: About the Quick Starts ## Article Link: https://dev.wix.com/docs/build-apps/get-started/quick-start/about-the-quick-starts.md ## Article Content: # About the Quick Start Tutorials Create your first app and get up and running in a few short steps. Wix offers [3 frameworks](https://dev.wix.com/docs/build-apps/get-started/overview/wix-s-development-frameworks.md) for building apps. The following tutorials walk you through the initial steps of creating a basic working app using each of these frameworks. To keep things simple, each tutorial showcases only one type of [app functionality](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md). ## Choose a framework * [Wix Blocks tutorial](https://dev.wix.com/docs/build-apps/get-started/quick-start/create-an-app-with-wix-blocks.md): Design, code, and deploy a site widget using Wix’s native app editor. * [Wix CLI tutorial](https://dev.wix.com/docs/build-apps/get-started/quick-start/create-an-app-with-the-wix-cli.md): Code and deploy a dashboard page with Wix’s React/Node.js stack. * [Self-managed app tutorial](https://dev.wix.com/docs/build-apps/get-started/quick-start/create-a-self-hosted-app.md): Set up a dashboard page in the app dashboard using an externally-deployed iframe. > **Note**: You can also start by trying out one of our many [tutorials](https://dev.wix.com/docs/build-apps/get-started/tutorials/index-of-tutorials.md) or [app templates](https://dev.wix.com/docs/build-apps/get-started/templates/get-started-from-an-app-template.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create an App with the Wix CLI ## Article: Quick Start a Wix CLI App ## Article Link: https://dev.wix.com/docs/build-apps/get-started/quick-start/create-an-app-with-the-wix-cli.md ## Article Content: # Quick Start a Wix CLI App This guide explains the minimum steps required to get a CLI app up and running using the Wix CLI. Prefer to watch instead? Here's the video walkthrough:
> **Note:** To develop with AI assistance, install the [Wix Plugin](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-the-wix-plugin.md) for your IDE. It adds [Wix Skills](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-wix-skills.md) and the [Wix MCP](https://dev.wix.com/docs/sdk/articles/use-the-wix-mcp/about-the-wix-mcp.md), giving your AI client the context it needs to help you build CLI projects. ## Before you begin Before getting started, make sure that you: + Have [Node.js](https://nodejs.org/en/) (v20.11.0 or higher). + Have Git installed and [configured](https://git-scm.com/docs/git-config). + Are logged into your Wix account. If you don't already have one, [sign up for a Wix account](https://manage.wix.com/account/custom-apps). ## Step 1 | Create a new app project 1. Run the following command to create a new app project: ```bash npm create @wix/new@latest app ``` 2. When asked what you would like to do, select **Create a new Wix App**. 3. Enter a **name for your app** and a **folder name** for your project. + The **app name** is the name that appears in your [app's dashboard](https://manage.wix.com/account/custom-apps). + The **folder name** is the name of the directory containing your project's local files. 4. Enter a **namespace** for your app. A namespace is a unique way to identify your app, in the format `@prefix/suffix`. Some extensions, such as [data collections](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/data-collections/about-data-collections-extensions.md) and [schema plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md), require a namespace. Wix suggests a namespace based on your app name. You can accept the suggestion or enter your own value. Once set, you can't change your namespace. 5. Enter a **code identifier** for your app. The code identifier is a unique, JavaScript-compatible name for your app and its extensions. Wix suggests a code identifier based on your namespace. You can accept the suggestion or enter your own value. Once set, you can't change your code identifier. Once the CLI has created your app, the [app's files](https://dev.wix.com/docs/wix-cli/guides/project-structure/project-structure.md) appear in a local directory with the folder name you chose. At this point your app appears in the [Wix app dashboard](https://manage.wix.com/account/custom-apps), but it isn't yet installed on a site for testing. ## Step 2 | Test the app 1. Run the [`dev`](https://dev.wix.com/docs/wix-cli/command-reference/project-commands/dev.md) command to start the local development environment: ```bash wix dev ``` The CLI builds a local environment for your test site and provides a menu to view your app's dashboard pages in the browser. Any changes you make to your code are immediately reflected in the browser. 2. Select a site to install your app on for testing: + **Pick an existing site:** Designates one of your existing Wix sites. + **Create a new Development Site:** Builds a new Wix [development site](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md#development-sites). 3. Press `Enter` to open your browser and install the application on your test site. 4. In your IDE, open `src/extensions/dashboard/pages/my-page/my-page.tsx` and edit the `title` prop in `` to any value you like. > **Note:** Return to your browser, and see the change reflected immediately. ## Step 3 | Call a Wix API In this step, we show how to call an API from your app's dashboard page, using the [List Locations](https://dev.wix.com/docs/api-reference/business-management/locations/list-locations?apiView=SDK.md) method as an example. 1. Add the **Manage Locations** permission to your app. To do so, go to **Permissions** in your app dashboard, click **Add Permissions**, search for **Manage Locations**, select it and save. [Permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md) define what data your app can access on an installed site. > **Note:** After you save permission changes in the app dashboard, they apply automatically to a development site. You don't need to build or release a new app version, but it may take a few minutes for the updated permissions to take effect. 2. Install the `@wix/business-tools` package so you can work with the [Locations API](https://dev.wix.com/docs/api-reference/business-management/locations/introduction?apiView=SDK.md). ```bash npm install @wix/business-tools ``` 3. Update the code of your dashboard page extension to display a list of all the business locations in your site: 1. Open your dashboard extension's tsx file. The path should be `/src/extensions/dashboard/pages/my-page/my-page.tsx`. 2. Add the following import statements to your file: ```js import { locations } from '@wix/business-tools'; import React, { useState, useEffect } from 'react'; ``` 3. In the `DashboardPage` component add a `locationNames` state variable and a `useEffect` hook that calls `listLocations()` and extracts the list of location names to `locationNames`. Your code should look like this: ```tsx const { listLocations } = locations; const [locationNames, setLocationNames] = useState(''); useEffect(() => { listLocations().then(response => { if (response.locations) { const names = response.locations.map(location => location.name).join('\n'); setLocationNames(names); } }); }, []); ``` 4. In the `Page.Content` element of your component, change the `title` to `"Locations List"` and the `subtitle` to the value of `locationNames`. Your code should look like this: ```tsx ``` **Full example code** ```tsx import type { FC } from 'react'; import { useState, useEffect } from 'react'; import { EmptyState, Page, WixDesignSystemProvider } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import { locations } from '@wix/business-tools'; const DashboardPage: FC = () => { const { listLocations } = locations; const [locationNames, setLocationNames] = useState(''); useEffect(() => { listLocations().then(response => { if (response.locations) { const names = response.locations.map(location => location.name).join('\n'); setLocationNames(names); } }); }, []); return ( ); }; export default DashboardPage; ``` Your dashboard page is now set up to load and display the names of the site's locations. 4. Test the API call: 1. Run the [`dev`](https://dev.wix.com/docs/wix-cli/command-reference/project-commands/dev.md) command to start the local development environment: ```bash wix dev ``` 2. Click the link to open your development site's dashboard. 3. Navigate to your app's page in the dashboard under Apps. ![Select app page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/89eea886639f9c7ea33f4fd878b74007.png) 4. Your app's dashboard page should load and display a list of locations. ![Locations app page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/373ec501c09c732cea74f800fd7a93b8.png) ## Next steps After completing the above steps, you have a simple Wix app that you can experiment with and test locally. You can now: + [Add extensions to your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/about-extensions-in-the-wix-cli.md) + [Learn more about developing your app](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) + [Install the Wix Plugin](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-the-wix-plugin.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Self-Managed App ## Article: Create a Self-Hosted App ## Article Link: https://dev.wix.com/docs/build-apps/get-started/quick-start/create-a-self-managed-app.md ## Article Content: # Tutorial | Create a Self-Managed App In this tutorial, you'll create an app with a simple [dashboard page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) using a [self-managed](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting-for-wix-apps.md) iframe. ## Before you begin * Make sure you're logged into your Wix account, or [create an account](https://www.wix.com/) if you don't have one yet. * If you haven't done so yet, move to the new [Wix Studio workspace](https://manage.wix.com/account/custom-apps) by [joining Wix Studio](https://support.wix.com/en/article/wix-studio-switching-to-wix-studio). ## Step 1 | Create an app in the Custom Apps page of your Wix Studio workspace We'll start off by creating a new app. 1. In the [Custom Apps page](https://manage.wix.com/account/custom-apps), go to **My Apps**, and then click **Create New App**. 2. Select **Build from scratch**. 3. Select **Self-Managed**, and then click **Get Started**. ![choose-app-framework](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f834d79429fff1627157c1cb4071fabe.png) A new app is created in the Custom Apps page. ## Step 2 | Add a dashboard page extension We'll now add a dashboard page extension in your app's dashboard. This extension uses an iframe to embed a web page in the dashboard. We've already deployed a web page on an external server for you to use in this tutorial. When configuring the extension, you'll register the URL of this web page. 1. In the left sidebar, under **Develop**, select **Extensions**, and then click **Create Extension**. A panel opens, showing the available extension types. 2. Find the **Dashboard page** extension and click **Create**. 3. Fill in the following configuration data: * **Name**: `Demo page` * **iframe URL**: `https://www.quickstartselfhostedapp.com/` 4. Click **Save**. ![iframe-url](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f2f1e4ce6ddc5fee7b50bb7e7e3b52dc.png) You’ve finished setting up the dashboard page extension and can now install it on a site to see it in action. ## Step 3 | Install your app on a site Wix provides a free Premium development site so you can easily install your app and see it working. In this step, you'll create a development site, install your app on the site, and try it out. 1. In the top right corner of your [app's dashboard](https://manage.wix.com/account/custom-apps), click **Test App** and select **Test on dev site**. 1. Select an existing development site or click **+ Create Dev Site** to create a new site. Select the editor and the Wix Business Solution you want to use and click **Create Dev Site**. 1. Click **Test App**. Wix installs your app and opens the site in a new tab. You can set which site page opens in your [app settings](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fapp-settings). If you don't set a page, the site editor opens by default. Your app is now installed on the development site. You can see the external web page embedded in the dashboard. ![self-hosted-iframe](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84c1722c069c39693ea2316875639fac.png) ## Next steps You now have a fully working app that can be installed on Wix sites. Take some time to play around in your app's dashboard to explore more features that you can add to your app. Use the following resources to continue building your app: * [Wix Design System](https://wixdesignsystem.com): Learn how to use Wix's React components for a cohesive user experience consistent with Wix's design standards. * [Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md): Learn how to integrate your app with Wix to transfer data between your app and the site it's installed on and extend Wix business solutions, such as Wix eCommerce or Wix Bookings. * [Extension catalog](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md#extension-catalog): Learn about other extensions you can add to your app. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create an App with Wix Blocks ## Article: Create an App with Wix Blocks ## Article Link: https://dev.wix.com/docs/build-apps/get-started/quick-start/create-an-app-with-wix-blocks.md ## Article Content: # Tutorial | Create an App in Wix Blocks In this tutorial, you'll create an app with a simple [site widget extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md). ## Before you begin * Make sure you're logged into your Wix account, or [create an account](https://www.wix.com/) if you don't have one yet. * If you haven't done so yet, move to the new [Wix Studio workspace](https://manage.wix.com/account/custom-apps) by [joining Wix Studio](https://support.wix.com/en/article/wix-studio-switching-to-wix-studio). * Make sure you have a Wix site that you can use for testing purposes. If you don’t have one yet, [create a new site](https://www.wix.com/website/templates/html/blank). ## Step 1 | Open the Wix Studio workspace and create an app You can access Wix Blocks through the Wix Studio workspace. 1. Open the [Custom Apps](https://manage.wix.com/account/custom-apps) section of the Wix Studio workspace. 1. Click **Create New App**. 1. Click **Build from scratch**. 1. Click **Wix Blocks**. Your newly-created app opens in Wix Blocks. ## Step 2 | Design and code a site widget We'll now build an interactive site widget in Blocks. 1. To get started, select **Blank canvas**. The Wix Blocks editor opens with a default widget. 2. Let's start by adding a text element to the widget. Click **Add Elements** ![add-elements-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/11a200c11c4e62ec09419ac5aa424a6c.png) at the top of the editor, and then click **Quick Add > Title**. Your widget now looks something like this: ![text-element](https://wixmp-833713b177cebf373f611808.wixmp.com/images/797662e82632425f7bf592e9c49a6a81.png) 3. Since we'll be interacting with the elements using code, we need some way to identify them. We do that using element IDs. You can see an element's ID when you hover over it or select it. When an element is selected you can change its ID using the **Properties & Events** panel. Change the text element's ID to be `message`. ![text-id-message](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d3474a44aa69bcfdaba500da6241f487.png) 1. To interact with an element in code, you first need to write code to select it. Now that our element has a unique identifier, selecting it with code is easy. We use the `$w()` selector function to select an element by ID. We simply pass the element's ID, preceded by a hashtag (`#`). So, to select an element with the ID `elementId`, we write `$w('#elementId')`. Once an element is selected, we can use its properties and functions to interact with it. So, we can use the text element's text property to change the message like this: `$w('#message').text = 'Hello World';` Place this code in the `onReady` event handler that appears in the Code Panel by default. Code in the `onReady` event handler runs during the widget loading process when all the elements in the widget have finished loading. The code should look like this: ```js $w.onReady(function () { $w('#message').text = 'Hello World'; }); ``` 1. Next, let's run some code in response to a button on our widget being clicked. ​First, we'll need to add a button to the widget. Click **Add Elements** ![add-elements-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/11a200c11c4e62ec09419ac5aa424a6c.png) on the top of the editor, and then click **Quick Add > Button**. Give it the ID `button`. 1. We’ll change what the button says, just like we did for the text element. To change the text of a button, make sure to use the label property. The code to change the button's text should look like this together with the previous code: ```js $w.onReady(function() { $w('#message').text = 'Hello World'; $w('#button').label = 'Click Me'; }); ``` 2. Finally, we'll add code that responds to the button being clicked. We again start by using the `$w()` function to select the button. Then we use the `onClick()` function to define what happens when the button is clicked. In this case, let's change the contents of the text element when the button is clicked. Place the following code below the existing code: ```js $w('#button').onClick (() => { $w('#message').text = 'Hello from Blocks!'; }); ``` So, the complete code looks like this: ```js $w.onReady(function () { $w('#message').text = 'Hello World'; $w('#button').label = 'Click Me'; }); $w('#button').onClick(() => { $w('#message').text = 'Hello from Blocks!'; }); ``` 1. Click **Preview** ![blocks-preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4e435e7595b65453d64691c05a9128c2.png) at the top of the editor to preview your widget. When previewing the widget, click the button on the widget to see the text change. ## Step 3 | Install your app on a site We'll now install the app on a site to see how the widget behaves in the site editor. 1. In the top-right corner, click **Test**. When prompted to provide an app name, enter any name you like and then click **Save & Continue**. 2. Select the site that you'd like to test your app on. The selected site opens in the editor. 4. Find your app under **Custom Apps** and click **Install App**. When the app is installed, the **Add Elements** panel opens, showing your widget. 5. Click the widget to add it to the current page. The widget is now added to the site. You can drag it, resize it, and modify its design using the widget's action bar. ![widget-on-site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8c3fc67b162b00c5c2e7ec9b92a2d938.png) ## Next steps You now have a fully working app that can be installed on Wix sites. Take some time to play around with the widget in Blocks and try to add more features to your app. Use the following resources to continue building your app: * [Editor elements](https://support.wix.com/en/article/studio-editor-adding-elements-4240855): Learn about the types of elements that you can add to your widget. * [Coding in Blocks](https://support.wix.com/en/article/wix-blocks-about-coding-in-blocks): Learn how to add code to an individual widget, as well as code for the entire app. * [Editor experience](https://support.wix.com/en/article/wix-blocks-about-configuration): Learn how to control the way site creators interact with your widget in the editors. * [App templates](https://dev.wix.com/apps-templates?filter=blocks): Explore templates that demonstrate how to build Blocks apps. * [Extension catalog](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md#extension-catalog): Learn about other extensions you can add to your app. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Get Started From an App Template ## Article: Get Started From an App Template ## Article Link: https://dev.wix.com/docs/build-apps/get-started/templates/get-started-from-an-app-template.md ## Article Content: # Get Started From an App Template Want to build an app that enhances the functionality of Wix sites? Jumpstart your development with one of our app templates and fast-track your way to a fully functioning app. ## Why start with a template? Each of our templates provides a working foundational app that you're free to modify and build upon. By deploying a template as your starting point, you can quickly move from a concept to a fully functional app, saving valuable time and effort. Dive into our template collection and discover the possibilities for app creation. [](http://dev.wix.com/apps-templates) ![Template page screenshot](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1e1cda7191005f355157ba2566338066.png) ## Select the right template for your app Wix offers templates specifically designed for each of the following development frameworks: - Wix Blocks - Wix CLI - Self-managed apps integrated with the app dashboard Choose a template that matches your preferred framework to commence your project with ease.
Wix Blocks Templates
Build a native-style Wix app in a responsive drag-and-drop editor and incorporate sophisticated functionality with Velo.
Wix CLI Templates
Get a comprehensive developer experience with minimal setup, so you can focus on coding your app.
Self-Managed App Templates
Develop and host an app on any platform, and integrate with Wix using the app dashboard.
[See all our templates ](http://dev.wix.com/apps-templates) ## Template features The Wix app templates showcase a variety of features that apps can add to Wix sites, such as: - Pages or widgets on a live Wix site. - Dashboard pages or components for site admins. - Integration with 3rd-party services. - Site analytics through embedded scripts. ## Developer tools Each template uses a different set of Wix developer tools, including: - [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md) and [Velo](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/code-in-blocks/about-widget-code-in-blocks.md): Combine UI design in the Wix editor with coding for advanced features, offering a comprehensive app-building experience. - [Wix Design System](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md): Utilize Wix's collection of reusable React components to craft a visually stunning and user-friendly app interface. - [Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli-for-apps.md): Accelerate app development and deployment with a tool that simplifies initial setup and hosting. - [REST APIs](https://dev.wix.com/docs/rest.md) and [JavaScript SDK](https://dev.wix.com/docs/sdk.md): Access and manage site data effectively from your app, enabling sophisticated site interactions. - [Webhooks](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/events-and-webhooks/about-webhooks.md): Implement real-time responses to Wix Logs. - [Embedded scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md): Integrate custom analytics and additional functionalities seamlessly. - [App dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md): Access essential resources for app configuration, permission management, and Wix integration. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Set Up an App With the CLI ## Article: Tutorial | Set Up an App With the CLI ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-cli.md ## Article Content: # Tutorial | Set Up an App With the CLI This tutorial demonstrates how to use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) to build an app named Business Buddy. The app provides an AI assistant for products in a site's Wix Stores catalog, accessible from the site owner's dashboard.
__Important:__ The chat endpoint calls [OpenAI](https://platform.openai.com/) as its AI provider. To complete this tutorial, you'll need an OpenAI account with prepaid API credits. See [Step 9](#step-9--add-backend-http-endpoints) for setup details. The same pattern applies to any AI provider with an HTTP API (Anthropic, a self-hosted model, etc.).
Business Buddy consists of: - Two dashboard pages: a Product page for selecting a product and chatting with the AI about it, and a Settings page for configuring the AI's behavior. - A backend that handles chat requests and stores per-install AI behavior directives. By following this tutorial, you'll learn how to: - Build dashboard pages using the [Wix Design System](https://www.wixdesignsystem.com/), the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md), and CSS. - Work with apps built by Wix such as Stores using the [Wix JavaScript SDK](https://dev.wix.com/docs/sdk.md). - Add backend logic using [HTTP endpoints](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/backend/http-endpoints/about-http-endpoints.md) in your CLI project. - Identify the current app install on the backend to support per-install data. - Call an external AI service ([OpenAI](https://platform.openai.com/)) from your backend, with the API key managed as a Wix CLI environment variable. The end result will look like this: ![Business Buddy Product Chat](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c7c1fa9d1d25177e73f6dd480360550e.png) We'll use the following steps to build the Business Buddy app: 1. [Initialize the app](#step-1--initialize-the-app). 1. [Create the dashboard pages](#step-2--create-the-dashboard-pages). 1. [Run a local development server](#step-3--run-a-local-development-server). 1. [Install dependencies](#step-4--install-dependencies). 1. [Design the Product page](#step-5--design-the-product-page). 1. [Set up calls to Wix SDKs](#step-6--set-up-calls-to-wix-react-sdks). 1. [Set up communication with the backend](#step-7--set-up-communication-with-the-backend). 1. [Design the Settings page](#step-8--design-the-settings-page). 1. [Add backend HTTP endpoints](#step-9--add-backend-http-endpoints). 1. [Build and deploy the app](#step-10--build-and-deploy-the-app). ## Before you begin Before getting started, make sure that: - You install [Node.js](https://nodejs.org/en/) (v20.11.0 or higher). - You install [npm](https://www.npmjs.com/package/npm) or [yarn](https://www.npmjs.com/package/yarn). - You’re logged into your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). ## Step 1 | Initialize the app We use the Wix CLI to initialize our Business Buddy app. In the process of initializing our app, the Wix CLI automatically: - Creates a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps) of your Wix Studio workspace. - Sets up a new folder for your app in your local file system. The folder includes: - A `src` folder containing initial boilerplate code for an app with a dashboard page. - A `package.json` file containing your app dependencies. - Creates a local Git repository for your app. To initialize the app: 1. Open a terminal and navigate to the folder where you want to create your app. 2. Run the following command: ```bash npm create @wix/new@latest app ``` If prompted to install the `@wix/create-new` package, press `y`. 3. Select **Create a new Wix App**. 4. Select **Create a basic app**. 5. Enter a name for your app. Let’s name our app `Business Buddy`. 6. Press `Enter` to accept the default folder name (`business-buddy`). 7. When asked to configure Wix MCP for your IDE, select your preferred MCP. A green **Success** message confirms that your app has been registered in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 8. Press `Enter` to accept the default namespace (`business-buddy`). 9. Press `Enter` to accept the default code identifier (`business_buddy`). You now have a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps), a new folder in your local file system, and a local Git repository for developing and testing your app. ## Step 2 | Create the dashboard pages In the app, we want the dashboard pages to appear in the dashboard like this: ![Sidebar structure](https://wixmp-833713b177cebf373f611808.wixmp.com/images/442d546d4258fd8311a4c3c70a7c633d.png) To create this structure, we use the CLI's `generate` command to [generate dashboard page extensions](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/add-dashboard-page-extensions-with-the-wix-cli.md). This places dashboard page files in subfolders of the `dashboard/pages` folder. ### Create dashboard page files 1. Run the following command and follow the prompts to create a dashboard page extension: ```bash wix generate ``` 2. When prompted for the kind of extension, select **Dashboard Page**. 3. When prompted for a page title, enter **Product**. 4. When prompted for the page route, press `Enter` to accept the default (`product`). The route is the path that is appended to the dashboard base URL to access the dashboard page. Learn more about [page routes](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/add-dashboard-page-extensions-with-the-wix-cli.md). Upon completion, the extension files will be created in your local app files under the chosen route with the following structure: ```bash src └── extensions └── dashboard └── pages └── product ├── product.extension.ts └── product.tsx ``` For more information about these files, see [Dashboard Page Extension Files and Code](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/dashboard-page-extension-files-and-code.md). Each dashboard page is defined by a `product.extension.ts` file and a `product.tsx` file. The product.extension.ts file defines the page metadata and the `product.tsx` file defines the page UI and functionality. Each file pair exists in a folder that determines the route to a page. 5. Repeat the above steps for a second page: title **Settings**, route default `settings` (press `Enter` to accept).
**Tip:** The CLI's default scaffold also created a `my-page` extension at `src/extensions/dashboard/pages/my-page/`. You can leave it (it won't interfere with this tutorial) or delete the `my-page/` folder to keep the dashboard sidebar showing only the **Product** and **Settings** pages.
## Step 3 | Run a local development server Now that you’ve initialized the app, you can run a local development server to see the app in action, and view local changes as you develop it. To run a local development server: 1. Navigate to your newly created folder for your app. ```bash cd business-buddy ``` 1. Run the following command: ```bash wix dev ``` 1. The CLI prompts you to choose a development site (test site), which you’ll use throughout this tutorial to run and test your app. You can choose an existing Wix site as your development site, or create a new one. Let’s **Create a new Development Site**. The newly created development site is automatically named **Dev Sitex** followed by a number (for example, **Dev Sitex 12345**), and can be found in your Wix account’s list of sites. 1. Follow the prompt to open the app installation page in your default browser. If the browser doesn’t open, [install your app on your test site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md) manually and skip the next step. 1. Click **Agree & Add** to install your app on your development site. 1. In the terminal menu, select **Dashboard** to open your development site’s dashboard. 1. In the dashboard sidebar, click **Apps** to see your newly created app’s dashboard pages. We add the content of our app’s dashboard pages in step 5. Your app is now running on your development site. As you develop your app, any changes made to your app’s code are reflected in real time on your development site. If your changes don’t show up, try refreshing the page, or closing and reopening the development site. ## Step 4 | Install dependencies Before we start coding our app, we need to install some npm packages. In your terminal, run the following commands: ```bash npm install @wix/wix-ui-icons-common npm install @wix/stores npm install @tanstack/react-query npm install openai ``` > **Note:** The `@wix/dashboard`, `@wix/design-system`, and `@wix/essentials` packages come pre-installed in the project that the CLI generated, so you don't need to install them. The purpose of each of these packages will become clear as we progress. ## Step 5 | Design the Product page In our app, we want the **Product** page to look like this: ![Business Buddy Product Chat](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c7c1fa9d1d25177e73f6dd480360550e.png) This design is consistent with the rest of the pages in the dashboard. To create it, we’ll use the React components provided by the [Wix Design System](https://www.wixdesignsystem.com/). The **Product** page is built from: - 2 main React components. - One wrapper component for initializing React context providers. - CSS styling. The **Product** page UI is defined in several files in `src/extensions/dashboard` as follows: - `/pages/product/product.tsx`: The header and the top cell that it used for selecting the product to chat about. - `/pages/product/ProductChat.tsx`: The bottom cell that displays the selected product and the chat. - `/pages/product/ProductChat.module.css`: CSS for the ProductChat component. - `/withProviders.tsx`: A wrapper that provides `WixDesignSystemProvider` for WDS styling and [`QueryClientProvider`](https://tanstack.com/query/v4/docs/framework/react/reference/QueryClientProvider) for data fetching. Let's take a look at the code used to build the page's UI. We'll take a look at the code for the page's functionality a bit later. ### product.tsx Notice how the code uses components from the Wix Design System to build the page. Other than that, it's a standard React component. For more information about each component, see [AutoComplete](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--autocomplete), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card-content), [TableActionCell](https://www.wix-pages.com/wix-design-system/?path=/story/components-lists-table--tableactioncell), [Divider](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--divider), [Layout](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--layout), and [Page](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page). > **Note:** At this point, we're showing the code with a hardcoded, dummy product object just to demonstrate how the page will look once a product is selected. We'll add real products to the page later in the tutorial. Open `src/extensions/dashboard/pages/product/product.tsx` and replace its contents with the following: ```tsx import { AutoComplete, Card, Cell, Divider, Layout, Page, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import React from 'react'; import { withProviders } from '../../withProviders'; import { ProductChat } from './ProductChat'; export default withProviders(function ProductPage() { const [currentProduct, setCurrentProduct] = React.useState({ name: 'Test Name', sku: 'Test SKU', }); const [searchQuery, setSearchQuery] = React.useState(''); return ( {currentProduct && } ); }); ``` ### ProductChat.tsx Here again, the code uses Wix Design System components to build the component UI. For more information about each component, see [Text](https://www.wix-pages.com/wix-design-system/?path=/story/components-typography-text--text), [Box](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--box), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card), [Input](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--input), and [Loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader). Create a new file at `src/extensions/dashboard/pages/product/ProductChat.tsx` with the following content: ```tsx import { Text, Box, Card, Input, Loader } from '@wix/design-system'; import { products } from '@wix/stores'; import React from 'react'; import * as Icons from '@wix/wix-ui-icons-common'; import styles from './ProductChat.module.css'; type Message = { author: 'Business Buddy' | 'User'; text: string; }; export function ProductChat(props: { product: products.Product }) { const [isWaitingForBusinessBuddy, setIsWaitingForBusinessBuddy] = React.useState(false); const [messageDraft, setMessageDraft] = React.useState( undefined ); const [chatMessages, setChatMessages] = React.useState([] as Message[]); return ( } placeholder='Ask Business Buddy something...' onChange={(e) => { setMessageDraft(e.target.value); }} value={messageDraft} /> {chatMessages.map((message) => ( {message.author}: {message.text} ))} {isWaitingForBusinessBuddy && ( )} ); } ``` ### ProductChat.module.css This file defines the width of the input element for the `ProductChat` component. Create a new file at `src/extensions/dashboard/pages/product/ProductChat.module.css` with the following content: ```css .userInput { width: 100%; } ``` #### withProviders.tsx This file wraps components with `WixDesignSystemProvider` for WDS styling and `QueryClientProvider` for data fetching. Create a new file at `src/extensions/dashboard/withProviders.tsx` with the following content: ```tsx import React from 'react'; import { WixDesignSystemProvider } from '@wix/design-system'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; export function withProviders(Component: React.ComponentType) { return function () { return ( ); }; } ``` ## Step 6 | Set up calls to Wix React SDKs In our app, on the **Product** page, users can choose a product from the site's Store. To work with the Stores app on a user's site, we use the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) and the [Wix SDK](https://dev.wix.com/docs/sdk/core-modules/sdk/introduction.md). We already showed how to set up your dashboard page extensions with the Dashboard SDK in [the previous section](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-cli.md). In this section, we'll use the SDK to retrieve site data to display in the app. Install Wix Stores on your development site and make sure the store has at least one product. ### Permissions Before getting started making calls with the SDK, you need to request the proper [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md). The permissions your app needs are listed in the **Permissions** section of each API reference page. Our app calls [`queryProducts`](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/query-products.md), which requires the **Read Products** permission. To add the permission: 1. Open the [`queryProducts` API reference](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/query-products.md) and scroll to the **Permissions** section. 2. Click the **READ PRODUCTS** badge. An app selector opens. 3. Select your app. The Permissions page for your app opens. 4. Click **Add Permissions**, search for **Read Products**, and add it. ### Get products After setting up permissions, our app can retrieve a list of products from the site's store. In `src/extensions/dashboard/pages/product/product.tsx`, add the following imports: ```tsx import { products } from '@wix/stores'; import { useQuery } from '@tanstack/react-query'; ``` These imports give us access to the functionality we need for retrieving products. Once we import everything, we’ll add the following hooks to our ProductPage component: 1. We use the React `useQuery()` hook to query for products whose names start with a query string. This string will be set based on what the user types in our `AutoComplete` component. We use the V1 `queryProducts()` builder pattern: call `queryProducts()` with no arguments, chain `.startsWith()` to filter by name, then call `.find()` to execute the query. We also make sure to handle any errors. ```tsx const { data: productsList, isLoading, error, } = useQuery({ queryKey: ['products', searchQuery], queryFn: () => products.queryProducts() .startsWith('name', searchQuery) .find(), }); if (error) return
Something went wrong
; ``` 2. Finally, let’s replace our dummy current product with the proper type now that we have it. ```tsx const [currentProduct, setCurrentProduct] = React.useState< products.Product | undefined >(); ``` ### Populate products Now that we have access to a list of the site’s products, we can use them to populate the `AutoComplete` component and define what happens when a user selects a product. 1. First, we set the `AutoComplete` component's `status`. ```tsx status={isLoading ? 'loading' : undefined} ``` 2. Then, we set the `AutoComplete` component's `options` to the products we got from the query by mapping them to a list of options objects with `id` and `value` properties. ```tsx options={productsList?.items.map((product) => ({ id: product._id!, value: product.name, }))} ``` 3. Next, we set up the `AutoComplete` component's `onSelect` function to set the component's current product. ```tsx onSelect={(e) => { setCurrentProduct( productsList!.items.find( (product) => product._id === (e.id as string) ) ); }} ``` While we're at it, we also set the `AutoComplete` component's `onChange` function to set the search query and clear the current product. ```tsx onChange={(e) => { setSearchQuery(e.target.value); setCurrentProduct(undefined); }} ``` 4. Finally, we set the `AutoComplete` component's `value` to the current product if there is one. ```tsx value={currentProduct?.name ?? undefined} ``` After completing the steps above, your `src/extensions/dashboard/pages/product/product.tsx` file should look like this: ```tsx import { AutoComplete, Card, Cell, Divider, Layout, Page, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import React from 'react'; import { products } from '@wix/stores'; import { useQuery } from '@tanstack/react-query'; import { withProviders } from '../../withProviders'; import { ProductChat } from './ProductChat'; export default withProviders(function ProductPage() { const [currentProduct, setCurrentProduct] = React.useState< products.Product | undefined >(); const [searchQuery, setSearchQuery] = React.useState(''); const { data: productsList, isLoading, error, } = useQuery({ queryKey: ['products', searchQuery], queryFn: () => products.queryProducts() .startsWith('name', searchQuery) .find(), }); if (error) return
Something went wrong
; return ( ({ id: product._id!, value: product.name, }))} onSelect={(e) => { setCurrentProduct( productsList!.items.find( (product) => product._id === (e.id as string) ) ); }} onChange={(e) => { setSearchQuery(e.target.value); setCurrentProduct(undefined); }} value={currentProduct?.name ?? undefined} /> {currentProduct && } ); }); ``` ## Step 7 | Set up communication with the backend In our app, the **Product** and **Settings** dashboard pages communicate with backend [HTTP endpoints](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/backend/http-endpoints/about-http-endpoints.md): one for chat messages and one for the AI behavior settings. The endpoints live in the same CLI project, in `src/pages/api/`. We set them up in Step 9; in this step, we set up the frontend code that calls them. When you call your endpoint with [`httpClient.fetchWithAuth()`](https://dev.wix.com/docs/sdk/core-modules/essentials/http-client.md) from [`@wix/essentials`](https://dev.wix.com/docs/sdk/core-modules/essentials/introduction.md), the Wix user's access token is attached to the request automatically, and the backend can extract the app instance ID from it without you having to pass it manually. ### Send the chat request We use the `ProductChat` component to send and receive chat messages. Open `src/extensions/dashboard/pages/product/ProductChat.tsx` and make the following changes: 1. Import `httpClient` from `@wix/essentials`. ```tsx import { httpClient } from '@wix/essentials'; ``` 2. Add a `submitMessage` function inside the `ProductChat` component. This function sends the user's message to the `/api/chat/product` endpoint and updates the chat state with the response. ```tsx async function submitMessage() { const newMessage: Message = { author: 'User', text: messageDraft ?? '', }; setChatMessages((state) => [...state, newMessage]); setMessageDraft(''); setIsWaitingForBusinessBuddy(true); const baseApiUrl = new URL(import.meta.url).origin; const res = await httpClient.fetchWithAuth( `${baseApiUrl}/api/chat/product`, { method: 'POST', body: JSON.stringify({ messages: [...chatMessages, newMessage], product: JSON.stringify(props.product, null, 2), }), } ); const { message } = await res.json(); setChatMessages((state) => [ ...state, { author: 'Business Buddy', text: message, }, ]); setIsWaitingForBusinessBuddy(false); } ``` We build the full endpoint URL with `new URL(import.meta.url).origin` so it works in both local development and production. 3. Wire up `submitMessage` to the send icon and to the Enter key on the `Input` component. ```tsx ``` ```tsx onEnterPressed={submitMessage} ``` After completing the steps above, your `src/extensions/dashboard/pages/product/ProductChat.tsx` file should look like this: ```tsx import { Text, Box, Card, Input, Loader } from '@wix/design-system'; import { products } from '@wix/stores'; import React from 'react'; import * as Icons from '@wix/wix-ui-icons-common'; import styles from './ProductChat.module.css'; import { httpClient } from '@wix/essentials'; type Message = { author: 'Business Buddy' | 'User'; text: string; }; export function ProductChat(props: { product: products.Product }) { const [isWaitingForBusinessBuddy, setIsWaitingForBusinessBuddy] = React.useState(false); const [messageDraft, setMessageDraft] = React.useState( undefined ); const [chatMessages, setChatMessages] = React.useState([] as Message[]); async function submitMessage() { const newMessage: Message = { author: 'User', text: messageDraft ?? '', }; setChatMessages((state) => [...state, newMessage]); setMessageDraft(''); setIsWaitingForBusinessBuddy(true); const baseApiUrl = new URL(import.meta.url).origin; const res = await httpClient.fetchWithAuth( `${baseApiUrl}/api/chat/product`, { method: 'POST', body: JSON.stringify({ messages: [...chatMessages, newMessage], product: JSON.stringify(props.product, null, 2), }), } ); const { message } = await res.json(); setChatMessages((state) => [ ...state, { author: 'Business Buddy', text: message, }, ]); setIsWaitingForBusinessBuddy(false); } return ( } placeholder='Ask Business Buddy something...' onChange={(e) => { setMessageDraft(e.target.value); }} onEnterPressed={submitMessage} value={messageDraft} /> {chatMessages.map((message) => ( {message.author}: {message.text} ))} {isWaitingForBusinessBuddy && ( )} ); } ``` ## Step 8 | Design the Settings page Open `src/extensions/dashboard/pages/settings/settings.tsx` and replace its contents with the following: ```tsx import { Button, Card, FormField, InputArea, Loader, Page, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import React from 'react'; import { useMutation, useQuery } from '@tanstack/react-query'; import { httpClient } from '@wix/essentials'; import { withProviders } from '../../withProviders'; import { dashboard } from '@wix/dashboard'; export default withProviders(function SettingsPage() { const { data } = useQuery<{ behaviorDirective: string; }>({ queryKey: ['settings'], queryFn: async () => { const baseApiUrl = new URL(import.meta.url).origin; const res = await httpClient.fetchWithAuth(`${baseApiUrl}/api/settings`); return res.json(); }, }); const mutation = useMutation({ mutationFn: async (newBehaviorDirective: string) => { const baseApiUrl = new URL(import.meta.url).origin; await httpClient.fetchWithAuth(`${baseApiUrl}/api/settings`, { method: 'POST', body: JSON.stringify({ behaviorDirective: newBehaviorDirective }), }); }, onSuccess: () => { dashboard.showToast({ message: 'Changes saved!', type: 'success', }); }, }); const [behaviorDirective, setBehaviorDirective] = React.useState(''); return ( { mutation.mutate(behaviorDirective); }} disabled={mutation.isPending} > {mutation.isPending ? : 'Save'} } > setBehaviorDirective(e.target.value)} /> ); }); ``` As usual, the code uses Wix Design System components to build the component UI. For more information about each component, see [Button](https://www.wix-pages.com/wix-design-system/?path=/story/components-actions--button), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card), [FormField](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--formfield), [InputArea](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--inputarea), [Loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader), and [Page](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page). ### @tanstack/react-query In this code, we import `useMutation` and `useQuery` from `@tanstack/react-query`: - **`useQuery`** is used to fetch data from the server and handle caching. `useQuery` fetches the current `behaviorDirective` from the server and stores it in `data`. ```tsx const { data } = useQuery<{ behaviorDirective: string; }>({ queryKey: ['settings'], queryFn: async () => { const baseApiUrl = new URL(import.meta.url).origin; const res = await httpClient.fetchWithAuth(`${baseApiUrl}/api/settings`); return res.json(); }, }); ``` - **`useMutation`** is used to update data on the server. `useMutation` sends a new `behaviorDirective` to the server via a POST request: ```tsx const mutation = useMutation({ mutationFn: async (newBehaviorDirective: string) => { const baseApiUrl = new URL(import.meta.url).origin; await httpClient.fetchWithAuth(`${baseApiUrl}/api/settings`, { method: 'POST', body: JSON.stringify({ behaviorDirective: newBehaviorDirective }), }); }, }); ``` ### Adding a toast When a user changes the behavior setting on the **Settings** page, we want to show a toast at the top of the dashboard page. To show toasts, update dashboard components based on state changes, and open and close dashboard modals, we use the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md). 1. Import the [`dashboard`](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) module from the Dashboard SDK. ```tsx import { dashboard } from '@wix/dashboard'; ``` 2. Call `showToast()` in the `onSuccess` callback of the mutation. This shows the toast when the **Save** button's request completes successfully. ```tsx onSuccess: () => { dashboard.showToast({ message: 'Changes saved!', type: 'success', }); }, ``` ## Step 9 | Add backend HTTP endpoints The app's backend logic, handling chat requests by calling the OpenAI API and storing the per-install AI behavior directive, runs as [HTTP endpoints](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/backend/http-endpoints/about-http-endpoints.md) inside our CLI project. There's no separate server. Each file we create in `src/pages/api/` is auto-discovered and exposed at `/api/`. No registration step is required. The `httpClient.fetchWithAuth()` call we used in Step 7 and Step 8 attaches the user's access token to each request automatically. On the server, we extract the current app install's ID from that token using [`auth.getTokenInfo()`](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md) from `@wix/essentials`. We don't need to set up CORS, manage an app secret key, or parse instance strings manually. This tutorial uses [OpenAI](https://platform.openai.com/) as the AI provider for the chat endpoint. The same pattern applies to any AI provider with an HTTP API, such as Anthropic, OpenRouter, and Hugging Face. Refer to your provider's documentation for the SDK and call shape. ### Set up your OpenAI API key 1. Go to [platform.openai.com](https://platform.openai.com/) and sign up. This is OpenAI's developer platform; it's separate from chatgpt.com, and an existing ChatGPT subscription doesn't grant API access. 2. Add a payment method and credits to your account. OpenAI API access is pay-per-use; the model used below (`gpt-4o-mini`) costs fractions of a cent per chat message, but OpenAI requires a pre-funded balance. Adding a payment method alone isn't enough. We recommend a small initial deposit and a low usage limit (e.g., $5) as a safety net. 3. Go to [platform.openai.com/api-keys](https://platform.openai.com/api-keys), click **Create new secret key**, give it a name, and click **Create**. Copy the key (it starts with `sk-...`). It's only displayed once. 4. Declare the key as a secret environment variable. Add an `env` block to `astro.config.mjs`: ```js import { defineConfig, envField } from 'astro/config'; // ...existing imports export default defineConfig({ // ...existing config env: { schema: { OPENAI_API_KEY: envField.string({ context: "server", access: "secret" }), } } }); ``` 5. Store the key with the Wix CLI and pull it locally: ```bash wix env set --key OPENAI_API_KEY --value sk-...your-key... wix env pull ``` `wix env set` uploads the key to Wix's servers; it's never written to a file in your project, so you can't accidentally commit it. `wix env pull` downloads it into your local environment so `wix dev` can use it. Run `wix env pull` again whenever you change the key on Wix's servers or pull the project to a new machine. For more, see [Manage Environment Variables](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/environment-variables/manage-environment-variables-in-the-cli.md).
__Tip:__ To use an OpenAI-compatible provider, set an `OPENAI_BASE_URL` environment variable using the same `wix env set` / `wix env pull` flow as above, and pass it as `baseURL` when constructing the `OpenAI` client. This works for providers such as Azure OpenAI, OpenRouter, and self-hosted models.
### Shared in-memory store Both endpoints we're about to create read and write the same AI behavior directive, keyed by app instance ID. We put the store in its own module so both endpoints can import it. Create the file `src/lib/directives-store.ts`: ```ts const directivesByInstance = new Map(); export function getDirective(instanceId: string): string | undefined { return directivesByInstance.get(instanceId); } export function setDirective(instanceId: string, directive: string): void { directivesByInstance.set(instanceId, directive); } ``` > **Note:** This in-memory store is for demonstration only. It loses data when the server restarts and isn't shared across server replicas. For production, replace it with a [Wix data collection](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/data-collections/about-data-collections-extensions.md) or your own external database. ### Settings endpoint The settings endpoint stores and retrieves the AI behavior directive for the current app install. The Settings page (Step 8) calls this endpoint via `GET` to load the saved directive and `POST` to save a new one. Create the file `src/pages/api/settings.ts`: ```ts import type { APIRoute } from 'astro'; import { auth } from '@wix/essentials'; import { getDirective, setDirective } from '../../lib/directives-store'; export const GET: APIRoute = async () => { const { instanceId } = await auth.getTokenInfo(); const behaviorDirective = getDirective(instanceId) ?? ''; return new Response( JSON.stringify({ behaviorDirective }), { headers: { 'Content-Type': 'application/json' } } ); }; export const POST: APIRoute = async ({ request }) => { const { instanceId } = await auth.getTokenInfo(); const { behaviorDirective } = await request.json(); setDirective(instanceId, behaviorDirective); return new Response(null, { status: 200 }); }; ``` `auth.getTokenInfo()` decodes the access token attached to the request by `httpClient.fetchWithAuth()` on the frontend, and returns information about the current install, including the `instanceId` we use as the storage key. Each app install gets its own directive. ### Chat endpoint The chat endpoint receives a chat message and product context from the **Product** page (Step 7), looks up the current install's behavior directive, calls OpenAI with the directive as a system prompt, and returns the assistant's reply. Create the file `src/pages/api/chat/product.ts`. The file's path under `src/pages/api/` (`chat/product.ts`) determines its URL: `/api/chat/product`. ```ts import type { APIRoute } from 'astro'; import { auth } from '@wix/essentials'; import { OPENAI_API_KEY } from 'astro:env/server'; import OpenAI from 'openai'; import { getDirective } from '../../../lib/directives-store'; type IncomingMessage = { author: 'Business Buddy' | 'User'; text: string; }; const openai = new OpenAI({ apiKey: OPENAI_API_KEY }); export const POST: APIRoute = async ({ request }) => { const { instanceId } = await auth.getTokenInfo(); const { messages, product } = (await request.json()) as { messages: IncomingMessage[]; product: string; }; const directive = getDirective(instanceId); const completion = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: 'You are Business Buddy, a helpful assistant who answers questions about a specific product in a Wix store.' }, ...(directive ? [{ role: 'system' as const, content: directive }] : []), { role: 'system', content: `Product context (JSON): ${product}` }, ...messages.map((m) => ({ role: (m.author === 'User' ? 'user' : 'assistant') as 'user' | 'assistant', content: m.text, })), ], }); const message = completion.choices[0]?.message?.content ?? "Sorry, I couldn't generate a response."; return new Response( JSON.stringify({ message }), { headers: { 'Content-Type': 'application/json' } } ); }; ``` The directive saved on the **Settings** page becomes a system prompt, the product info is passed as additional context, and the chat history is the conversation. Because the endpoint runs server-side, your API key never reaches the client. After completing the steps in this section, restart `wix dev` (kill the running process and re-run, because the schema change and the new `astro:env/server` import need a clean restart). Try a chat message in the **Product** page; you should get a real AI-generated reply that reflects the Business Buddy persona, talks about the selected product, and honors any directive you've set on the **Settings** page. ## Step 10 | Build and deploy the app After testing your app and seeing that it works as expected, you can create a public preview of your app, build your app, create and manage app versions, and deploy your app. For more information on these tasks, see [Build and Deploy an App with the CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/build-and-deploy.md) ## Summary After completing this tutorial, you have a working AI chatbot Wix CLI app with two dashboard pages, backend HTTP endpoints calling OpenAI, and per-install storage so each app instance can shape Business Buddy's behavior with its own directive. At this point, you have all you need to know to develop an app. Feel free to play around with the example app some more, or get started on writing your own app. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Build a Locations App with the CLI ## Article: Tutorial | Build a Locations App with the CLI ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-a-locations-app-with-the-cli.md ## Article Content: # Tutorial | Build a Locations App Using the Wix CLI This tutorial demonstrates how to use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) to build a Locations app on the [Wix Platform](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md). The app contains a dashboard page that lists a Wix site's business locations, providing a foundation you can build on with per-location actions like editing hours. By following this tutorial, you'll learn how to: - Build a dashboard page using the [Wix Design System](https://www.wixdesignsystem.com/). - Read business locations from a site using the [Wix Locations SDK](https://dev.wix.com/docs/api-reference/business-management/locations/introduction?apiView=SDK.md). - Navigate to a built-in Wix dashboard page from within your app, using the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md). The end result will look like this: ![Locations app tutorial final result](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fa8f2472f7e1bb26c84e9c44e3e5c460.png) We'll use the following steps to build the Locations app: 1. [Initialize the app](#step-1--initialize-the-app). 1. [Create the dashboard page](#step-2--create-the-dashboard-page). 1. [Run a local development server](#step-3--run-a-local-development-server). 1. [Develop the app](#step-4--develop-the-app). 1. [Test the app](#step-5--test-the-app). 1. [Build and deploy the app](#step-6--build-and-deploy-the-app). ## Before you begin Before getting started, make sure that: - You install [Node.js](https://nodejs.org/en/) (v20.11.0 or higher). - You install [npm](https://www.npmjs.com/package/npm) or [yarn](https://www.npmjs.com/package/yarn). - You’re logged into your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account.](https://manage.wix.com/account/custom-apps) ## Step 1 | Initialize the app We use the Wix CLI to initialize our Locations app. In the process of initializing our app, the Wix CLI automatically: - Creates a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps) of your Wix Studio workspace. - Sets up a new folder for your app in your local file system. The folder includes: - A `src` folder containing initial boilerplate code for an app with a dashboard page. - A `package.json` file containing your app dependencies. - Creates a local Git repository for your app. To initialize the app: 1. Open a terminal and navigate to the folder where you want to create your app. 2. Run the following command: ```bash npm create @wix/new@latest app ``` If prompted to install the `@wix/create-new` package, press `y`. 3. Select **Create a new Wix App**. 4. Select **Create a basic app**. 5. Enter a name for your app. Let’s name our app `My Locations App`. 6. Press `Enter` to accept the default folder name (`my-locations-app`). 7. When asked to configure Wix MCP for your IDE, select your preferred MCP. A green **Success** message confirms that your app has been registered in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 8. Press `Enter` to accept the default namespace (`my-locations-app`). 9. Press `Enter` to accept the default code identifier (`my_locations_app`). You now have a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps), a new folder in your local file system, and a local Git repository for developing and testing your app. ## Step 2 | Create the dashboard page We use the CLI's `generate` command to create a dashboard page for our app. To create the dashboard page: 1. Navigate to your newly created folder for your app. ```bash cd my-locations-app ``` 2. Run the following command and follow the prompts: ```bash wix generate ``` 3. When prompted for the kind of extension, select **Dashboard Page**. 4. When prompted for a page title, enter **Locations**. 5. When prompted for the page route, press `Enter` to accept the default (`locations`). Upon completion, the extension files are created at `src/extensions/dashboard/pages/locations/`: ```bash src └── extensions └── dashboard └── pages └── locations ├── locations.extension.ts └── locations.tsx ``` For more information about these files, see [Dashboard Page Extension Files and Code](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/dashboard-page-extension-files-and-code.md).
**Tip:** The CLI's default scaffold also created a `my-page` extension at `src/extensions/dashboard/pages/my-page/`. You can leave it (it won't interfere with this tutorial) or delete the `my-page/` folder to keep the dashboard sidebar showing only the **Locations** page.
## Step 3 | Run a local development server Now that you’ve initialized your app, you can run a local development server to see the app in action, and view local changes as you develop your app. To run a local development server for your app: 1. In the terminal, from your app's folder, run the following command: ```bash wix dev ``` 1. The CLI prompts you to choose a development site (test site), which you’ll use throughout this tutorial to run and test your app. You can choose an existing Wix site as your development site, or create a new one. Let’s **Create a new Development Site**. The newly created development site is automatically named **Dev Sitex** followed by a number (for example, **Dev Sitex 12345**), and can be found in your Wix account’s list of sites. 1. Follow the prompt to open the app installation page in your default browser. If the browser doesn’t open, [install your app on your test site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md) manually and skip the next step. 1. Click **Agree & Add** to install your app on your development site. 1. In the terminal menu, select **Dashboard** to open your development site’s dashboard. 1. In the dashboard sidebar, click **Apps > Locations** to see your newly created app’s dashboard page. We add the content of our app’s dashboard page in the next step. ![Locations app empty state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d73c4379e54243752cc4dfbee5b866c3.png) Your app is now running on your development site. As you develop your app, any changes made to your app’s code are reflected in real time on your development site. If your changes don’t show up, try refreshing the page, or closing and reopening the development site. ## Step 4 | Develop the app In this step, we show how we develop our app further by coding our dashboard page. We use the [`queryLocations()`](https://dev.wix.com/docs/api-reference/business-management/locations/query-locations?apiView=SDK.md) function in the [Wix SDK’s Locations API](https://dev.wix.com/docs/api-reference/business-management/locations/introduction?apiView=SDK.md) to get a list of our site’s business locations. Then we use React code to display the locations on our dashboard page. ### Add API permissions To use [`queryLocations()`](https://dev.wix.com/docs/api-reference/business-management/locations/query-locations?apiView=SDK.md) in our app, we need to give our app permission requirements in the app dashboard. Once we do this, anyone installing the app will be prompted to grant the specified permissions. Our app calls `queryLocations()` to read the site’s business locations, so it needs a read locations scope. The method’s reference page (linked above) lists the exact scope it requires in its **Permission Scopes** section, including the scope’s ID (`SCOPE.DC-MULTILOCATION.READ-LOCATIONS`). To add the scope: 1. Go to the [**Permissions** tab](https://manage.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fdev-center-permissions) in your app's dashboard. 1. Click **Add Permissions**. 1. Search for the scope. Several scopes can have similar names, so match the one whose ID matches the scope ID shown on the method's reference page. 1. Check its checkbox under **Choose Permission Scopes** and click **Save**. For more information on configuring permissions, see [Configure permissions for your app](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md). ### Install packages We use the following packages for coding and designing our app’s dashboard page: - **`@wix/business-tools`:** Business tools module for working with the Wix SDK [Locations API](https://dev.wix.com/docs/api-reference/business-management/locations/introduction?apiView=SDK.md). - **`@wix/dashboard`:** [Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) for interacting with the site dashboard. - **`@wix/design-system`:** The [Wix Design System](https://www.wixdesignsystem.com/) for designing our dashboard page using several design components. The `@wix/dashboard` and `@wix/design-system` packages come pre-installed in the project that the CLI generated, so you don’t need to install them. However, you do need to install the `@wix/business-tools` package. To do so, open your terminal and run the following command: ```bash npm install @wix/business-tools ``` ### Write the code We'll now write code to create a dashboard page for our app. We want the dashboard page to display a list of all the business locations in our site, and a button for editing each location’s hours of operation. 1. Open your app’s folder in your IDE. Then open the `locations.tsx` file located in `src/extensions/dashboard/pages/locations/`. 2. Delete the existing code in the file. 3. Add the following import statements to your file: ```tsx import { Box, Loader, Table, TableActionCell, EmptyState, Page, TextButton, WixDesignSystemProvider } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import { locations } from '@wix/business-tools'; import * as Icons from '@wix/wix-ui-icons-common'; import React, { useState, useEffect } from 'react'; import { dashboard } from '@wix/dashboard'; ``` 4. Next, use React and components from the [Wix Design System](https://www.wixdesignsystem.com/) to create a display table for the list of locations in the dashboard, or an empty state message with an option to add a location if there is no location data. The `useEffect` hook fetches the location data using the `queryLocations()` function and updates the component's state accordingly. If the data is still loading, it displays a loading spinner. You can see the complete code for this example below. **Full Example Code** ```tsx import { Box, Loader, Table, TableActionCell, EmptyState, Page, TextButton, WixDesignSystemProvider } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import { locations } from '@wix/business-tools'; import * as Icons from '@wix/wix-ui-icons-common'; import React, { useState, useEffect } from 'react'; import { dashboard } from '@wix/dashboard'; export default function Index() { const { navigate } = dashboard; const { queryLocations } = locations; const [isLoading, setIsLoading] = useState(true); const [businessLocations, setBusinessLocations] = useState([]); useEffect(() => { const fetchLocations = async () => { try { const result = await queryLocations().find(); // Filter the development site's default location. Remove this line before publishing your app. const filteredResults = result.items.filter((location: any) => location.name !== "Location 1"); setBusinessLocations(filteredResults); setIsLoading(false); } catch (error) { console.error('Error fetching locations:', error); setIsLoading(false); } }; fetchLocations(); }, []); if (isLoading) { return ; } const columns = [ { title: 'Name', render: (location: any) => location.name, }, { title: '', render: () => ( console.log('open modal'), }} />) } ]; return ( {businessLocations.length > 0 ?
: navigate('71e35f24-8eb7-41b0-b261-c2259a76372f')} prefixIcon={}> Add Location }
); } ``` You now have a fully developed app with a dashboard page for site owners to manage their business locations and hours of operation. ## Step 5 | Test the app In the previous step, we wrote code for displaying a site’s business locations in the dashboard. However, our development site doesn’t yet have any business location content for the app to display. We need to add business locations to our development site, and test our app to see whether the table with our business locations appears. To add business locations to your site: 1. Run a local development server for your app using the `wix dev` command in your terminal. Your app’s dashboard page will now look like this: ![Add locations to your site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/cdf71f12912bb644c1806d70fb488e61.png) 2. Click **Add Location**. This takes you to the Business Info Settings to add locations to your development site. 3. Add business locations to your development site. 4. Navigate back to your app’s dashboard page in the left sidebar. The page should now display a table with a list of the business locations that you just added. You now have a working app with a dashboard page that lists your site’s business locations, and a button for editing the hours of operation. ## Step 6 | Build and deploy the app After testing your app and seeing that it works as expected, you can [create a preview of your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/build-and-deploy.md), [build your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/build-and-deploy.md), [release and publish your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/build-and-deploy.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Custom Products Catalog App with the CLI ## Article: Tutorial | Create a Custom Products Catalog App with the CLI ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-custom-products-catalog-app-with-the-cli.md ## Article Content: # Tutorial | Create a Custom Products Catalog App with the CLI This tutorial demonstrates how to use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) to build a Custom Products Catalog app on the [Wix Platform](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md). The app contains a dashboard page that lists, searches, sorts, adds, and deletes products in a Wix Stores catalog. > **Note:** This tutorial uses the [Stores Catalog V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/introduction.md) only. This is sufficient for private apps. However, apps published to the Wix App Market should support both the [V1](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/introduction.md) and [V3](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/introduction.md) Stores Catalog APIs. For more details, see [Exposing Apps Publicly and Privately](https://dev.wix.com/docs/build-apps/get-started/overview/exposing-apps-publicly-and-privately.md). By following this tutorial, you'll learn how to: - Build a collection-style dashboard page (with toolbar, filters, sorting, bulk actions) using the [Wix Design System](https://www.wixdesignsystem.com/) and Wix Patterns. - Read, write, and bulk-delete products using the [Wix Stores Products V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/introduction?apiView=SDK.md). - Manage SDK call state with React Query. The end result will look like this: ![App preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2d80442fb96a66bfe7f0c50010094c9f.png) We'll use the following steps to build the Custom Products Catalog app: 1. [Initialize the app](#step-1--initialize-the-app). 1. [Create the dashboard page](#step-2--create-the-dashboard-page). 1. [Run a local development server](#step-3--run-a-local-development-server). 1. [Add permissions](#step-4--add-permissions). 1. [Set up Wix Stores](#step-5--set-up-wix-stores). 1. [Install dependencies](#step-6--install-dependencies). 1. [Set up wrappers for our dashboard page component](#step-7--set-up-wrappers-for-our-dashboard-page-component). 1. [Create hooks for our SDK calls](#step-8--create-hooks-for-our-sdk-calls). 1. [Create a "Create Product" modal](#step-9--create-a-create-product-modal). 1. [Create a dashboard page component](#step-10--create-a-dashboard-page-component). 1. [Test the app](#step-11--test-the-app). 1. [Build and deploy the app](#step-12--build-and-deploy-the-app). ## Before you begin Before getting started, make sure that: - You install [Node.js](https://nodejs.org/en/download/) (v20.11.0 or higher). - You install [npm](https://www.npmjs.com/package/npm) or [yarn](https://www.npmjs.com/package/yarn). - You're logged into your Wix Studio account. If you don't already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). We also recommend that you check out the [Patterns documentation](https://www.wix-pages.com/wix-patterns/?path=/story/getting-started--overview). Patterns is a complex React library. Familiarizing yourself with the basics will help you better understand the code in this tutorial. ## Step 1 | Initialize the app We use the Wix CLI to initialize our Custom Products Catalog app. In the process of initializing our app, the Wix CLI automatically: - Creates a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps) of your Wix Studio workspace. - Sets up a new folder for your app in your local file system. The folder includes: - A `src` folder containing initial boilerplate code for an app with a dashboard page. - A `package.json` file containing your app dependencies. - Creates a local Git repository for your app. To initialize the app: 1. Open a terminal and navigate to the folder where you want to create your app. 1. Run the following command: ```bash npm create @wix/new@latest app ``` If prompted to install the `@wix/create-new` package, press `y`. 1. Select **Create a new Wix App**. 1. Select **Create a basic app**. 1. Enter a name for your app. Let’s name our app `Custom Products Catalog`. 1. Press `Enter` to accept the default folder name (`custom-products-catalog`). 1. When asked to configure Wix MCP for your IDE, select your preferred MCP. A green **Success** message confirms that your app has been registered in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 1. Press `Enter` to accept the default namespace (`custom-products-catalog`). 1. Press `Enter` to accept the default code identifier (`custom_products_catalog`). You now have a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps), a new folder in your local file system, and a local Git repository for developing and testing your app. ## Step 2 | Create the dashboard page We use the CLI's `generate` command to create a dashboard page for our app. To create the dashboard page: 1. Navigate to your newly created folder for your app. ```bash cd custom-products-catalog ``` 2. Run the following command and follow the prompts: ```bash wix generate ``` 3. When prompted for the kind of extension, select **Dashboard Page**. 4. When prompted for a page title, enter **Custom Products Catalog**. 5. When prompted for the page route, press `Enter` to accept the default (`custom-products-catalog`). Upon completion, the extension files are created at `src/extensions/dashboard/pages/custom-products-catalog/`: ```bash src └── extensions └── dashboard └── pages └── custom-products-catalog ├── custom-products-catalog.extension.ts └── custom-products-catalog.tsx ``` For more information about these files, see [Dashboard Page Extension Files and Code](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/dashboard-page-extension-files-and-code.md).
**Tip:** The CLI's default scaffold also created a `my-page` extension at `src/extensions/dashboard/pages/my-page/`. You can leave it (it won't interfere with this tutorial) or delete the `my-page/` folder to keep the dashboard sidebar showing only the **Custom Products Catalog** page.
## Step 3 | Run a local development server Now that you’ve initialized your app, you can run a local development server to see the app in action, and view local changes as you develop your app. To run a local development server for your app: 1. In the terminal, from your app's folder, run the following command: ```bash wix dev ```
__Important:__ This tutorial uses Stores Catalog V3. New Wix sites use V3 by default. If you choose an existing site instead, confirm it uses V3 with the [Catalog Versioning API](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-versioning/introduction.md).
1. The CLI prompts you to choose a development site (test site), which you’ll use throughout this tutorial to run and test your app. You can choose an existing Wix site as your development site, or create a new one. Let’s **Create a new Development Site**. The newly created development site is automatically named **Dev Sitex** followed by a number (for example, **Dev Sitex 12345**), and can be found in your Wix account’s list of sites. 1. Follow the prompt to open the app installation page in your default browser. If the browser doesn’t open, [install your app on your test site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md) manually and skip the next step. 1. Click **Agree & Add** to install your app on your development site. 1. In the terminal menu, select **Dashboard** to open your development site’s dashboard. 1. In the dashboard sidebar, click **Apps > Custom Products Catalog** to see your newly created app’s dashboard page. We add the content of our app’s dashboard page in the next step. ![Dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ab40ce3d9bb15aee303fbe438715daa3.png) Your app is now running on your development site. As you develop your app, any changes made to your app’s code are reflected in real time on your development site. If your changes don’t show up, try refreshing the page, or closing and reopening the development site. ## Step 4 | Add permissions In this step, we'll add permissions for the app. Every SDK API requires specific permissions to use. In this app, we will use the [`searchProducts()`](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/search-products.md), [`createProduct()`](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/create-product.md), and [`deleteProduct()`](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/delete-product.md) functions in the [Wix Stores Products V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/introduction.md) to get a list of all products, and to add and delete them. To use these functions, we need to give our app permission requirements in the app dashboard. Once we do this, anyone installing the app will be prompted to grant the specified permissions. Our app calls `searchProducts()` to read products and `createProduct()`/`deleteProduct()` to add and remove them, so it needs both a read and a write product scope. Each method's reference page (linked above) lists the exact scope it requires in its **Permission Scopes** section, including the scope's ID (for example, `SCOPE.STORES.PRODUCT_READ`). To add a scope: 1. Go to the [**Permissions** tab](https://manage.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fdev-center-permissions) in your app's dashboard. 1. Click **Add Permissions**. 1. Search for the scope. Several scopes can have similar names, so match the one whose ID matches the scope ID shown on the method's reference page. 1. Check its checkbox under **Choose Permission Scopes** and click **Save**. 1. Repeat for each scope your methods require: a read scope for `searchProducts()`, and a write scope for `createProduct()` and `deleteProduct()`. For more information on configuring permissions, see [Configure permissions for your app](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md). ## Step 5 | Set up Wix Stores Our app integrates with Wix Stores, so we need to set up Wix Stores on our site, then reinstall the app so that our site requests the required permissions: 1. Install the [Wix Stores](https://www.wix.com/app-market/wix-stores?searchLocation=home) app on your test site. You can create some new products, or use the sample ones provided by Wix. 1. Open your app’s dashboard in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 1. Click **Test App**, then select **Test on dev site**. In the **Select a development site** dialog, choose your dev site and click **Test App**. The app is reinstalled on your dev site with the latest permissions. ## Step 6 | Install dependencies Before we start coding our app, we need to install some npm packages. The `@wix/dashboard` and `@wix/design-system` packages come pre-installed in the project that the CLI generated. In your terminal, run the following commands to install the remaining packages: ```bash npm install @wix/patterns npm install @wix/stores ``` The purpose of each of these packages will become clear as we progress. ## Step 7 | Set up wrappers for our dashboard page component It’s finally time to start writing some code! Our plan in this tutorial is to create a React component that defines our dashboard page. We’re going to wrap this component with providers that will manage our data fetching and provide Wix styling for our dashboard page component. To do this, we’ll create a higher-order component that will accept our dashboard page component and return it wrapped in the necessary providers. To create the higher-order component: 1. Create a new file in your app's repo under `src > extensions > dashboard` named `withProviders.tsx`. 1. Import `FC` from React, `WixDesignSystemProvider` from the design system, and `WixPatternsProvider` from Patterns: ```js import type { FC } from 'react'; import { WixDesignSystemProvider } from '@wix/design-system'; import { WixPatternsProvider } from '@wix/patterns/provider'; ``` - `WixDesignSystemProvider` provides styling for Wix Design System components. - `WixPatternsProvider` provides styling and data manipulation for Wix Patterns components. 1. Write a function named `withProviders` to wrap our dashboard page component in `WixDesignSystemProvider` and `WixPatternsProvider`: ```js export function withProviders

(Component: FC

) { return function WithProviders(props: P) { return ( ); }; } ``` Your complete **withProviders.tsx** file should look like this: ```js import type { FC } from 'react'; import { WixDesignSystemProvider } from '@wix/design-system'; import { WixPatternsProvider } from '@wix/patterns/provider'; export function withProviders

(Component: FC

) { return function WithProviders(props: P) { return ( ); }; } ``` ## Step 8 | Create hooks for our SDK calls Our dashboard page component will need to make SDK calls to create and delete Wix Stores product data using React hooks. To simplify our component code, we define these in a separate TypeScript file. To create your app’s React hooks: 1. Create a new folder in your app's repo under `src > extensions > dashboard` named `hooks`. 1. Create a new file in `src > extensions > dashboard > hooks` named `stores.ts`. 1. Import `productsV3` from Wix Stores so we can use the Wix Stores Products V3 API: ```js import { productsV3 } from '@wix/stores'; ``` 1. Import `useCallback`: ```js import { useCallback } from 'react'; ``` [`useCallback`](https://react.dev/reference/react/useCallback) is a react hook that caches a callback function, returning a memorized version of the function that changes only if one of the dependencies has changed. We use this hook to define how a new product is created and how a product is deleted using the `optimisticActions` prop and `createProduct`/`deleteProduct` from `productsV3`. Whenever any of these dependencies change, `useCallback` redefines the callback function. 1. Import `CollectionOptimisticActions`: ```js import { CollectionOptimisticActions } from '@wix/patterns'; ``` `CollectionOptimisticActions` is a class that provides a set of utilities for managing optimistic actions on a [Wix Collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection). It allows us to perform various actions, such as adding, updating, and deleting items from a collection while assuming that these actions will succeed, even before confirming with the server. Learn more about the [CollectionOptimisticActions class](https://www.wix-pages.com/wix-patterns/?path=/story/features-actions-updates--collectionoptimisticactions). 1. Create a function to create new products named `useCreateProduct`: ```js export function useCreateProduct(optimisticActions: CollectionOptimisticActions) {} ``` Inside this function: 1. Use `productsV3` to get the `createProduct()` function. ```js const { createProduct } = productsV3; ``` 1. Use `useCallback()` to manage our `createProduct()` call: ```js return useCallback((productName: string) => { const newProduct: productsV3.V3Product = { name: productName, productType: productsV3.ProductType.PHYSICAL, physicalProperties: {}, variantsInfo: { variants: [{ choices: [], price: { actualPrice: { amount: '10' }, }, }], }, }; optimisticActions.createOne(newProduct, { submit: async () => { const created = await createProduct(newProduct); return [created]; }, successToast: { message: `${newProduct.name} was successfully created`, type: 'SUCCESS', }, errorToast: () => 'Failed to create product', }); }, [optimisticActions, createProduct]); ``` > **Note:** The [Products V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/create-product.md) requires several fields on every product: > > - `productType` > - The matching properties block (`physicalProperties` when `productType` is `PHYSICAL`) > - `variantsInfo.variants` with at least one variant > > A production app would normally collect these values from the user. To keep the **Create Product** modal minimal, this tutorial only asks for a product name and hardcodes the rest in `useCreateProduct`, so every new product is a physical product priced at $10. Let's break down the above code: - **Lines 2-14**: Define the properties of the new product. Only `name` comes from user input; the other fields satisfy the V3 API's required-fields contract with fixed defaults. - **Lines 16-27**: Use `optimisticActions.createOne()` to create the new product. - **Lines 17-20**: Define the `submit` function. `createProduct` takes the V3 product object directly and returns the created product, so we pass `newProduct` from the closure and wrap the response in an array for the optimistic collection. - **Lines 21-24**: Define the toast to show when a product is created successfully. - **Line 25**: Define the toast to show when product creation fails. - **Line 28**: Define the dependencies for `useCallback()`. Your function should look like this: ```js export function useCreateProduct(optimisticActions: CollectionOptimisticActions) { const { createProduct } = productsV3; return useCallback((productName: string) => { const newProduct: productsV3.V3Product = { name: productName, productType: productsV3.ProductType.PHYSICAL, physicalProperties: {}, variantsInfo: { variants: [{ choices: [], price: { actualPrice: { amount: '10' }, }, }], }, }; optimisticActions.createOne(newProduct, { submit: async () => { const created = await createProduct(newProduct); return [created]; }, successToast: { message: `${newProduct.name} was successfully created`, type: 'SUCCESS', }, errorToast: () => 'Failed to create product', }); }, [optimisticActions, createProduct]); } ``` 1. Create a function to delete products named `useDeleteProducts()`: ```js export function useDeleteProducts(optimisticActions: CollectionOptimisticActions) {} ``` Inside this function: 1. Use `productsV3` to get the `deleteProduct()` function. ```js const { deleteProduct } = productsV3; ``` 1. Use `useCallback()` to manage our `deleteProduct()` calls: ```js return useCallback((productsToDelete: productsV3.V3Product[]) => { optimisticActions.deleteMany(productsToDelete, { submit: async (deletedProducts: productsV3.V3Product[]) => ( await Promise.all( deletedProducts.map((product) => deleteProduct(product._id!)) ) ), successToast: { message: `${ productsToDelete.length > 1 ? 'Products' : 'Product' } deleted successfully`, type: 'SUCCESS', }, errorToast: () => `Failed to delete ${ productsToDelete.length > 1 ? 'Products' : 'Product' }`, }); }, [optimisticActions, deleteProduct]); ``` Let's break down the above code: - **Lines 2-17**: Use `optimisticActions.deleteMany()` to delete the products. - **Lines 3-7**: Define the submit function to handle product deletion. This function iterates over the array of products to delete and calls `deleteProduct()` for each one. `deleteProduct` takes the product ID directly as a string. - **Lines 8-13**: Define the toast to show when products are deleted successfully. - **Lines 14-16**: Define the toast to show when product deletion fails. - **Line 18**: Define the dependencies for `useCallback()`. Your function should look like this: ```js export function useDeleteProducts(optimisticActions: CollectionOptimisticActions) { const { deleteProduct } = productsV3; return useCallback((productsToDelete: productsV3.V3Product[]) => { optimisticActions.deleteMany(productsToDelete, { submit: async (deletedProducts: productsV3.V3Product[]) => ( await Promise.all( deletedProducts.map((product) => deleteProduct(product._id!)) ) ), successToast: { message: `${ productsToDelete.length > 1 ? 'Products' : 'Product' } deleted successfully`, type: 'SUCCESS', }, errorToast: () => `Failed to delete ${ productsToDelete.length > 1 ? 'Products' : 'Product' }`, }); }, [optimisticActions, deleteProduct]); } ``` Your complete `stores.ts` file should look like this: ```js import { productsV3 } from '@wix/stores'; import { useCallback } from 'react'; import { CollectionOptimisticActions } from '@wix/patterns'; export function useCreateProduct(optimisticActions: CollectionOptimisticActions) { const { createProduct } = productsV3; return useCallback((productName: string) => { const newProduct: productsV3.V3Product = { name: productName, productType: productsV3.ProductType.PHYSICAL, physicalProperties: {}, variantsInfo: { variants: [{ choices: [], price: { actualPrice: { amount: '10' }, }, }], }, }; optimisticActions.createOne(newProduct, { submit: async () => { const created = await createProduct(newProduct); return [created]; }, successToast: { message: `${newProduct.name} was successfully created`, type: 'SUCCESS', }, errorToast: () => 'Failed to create product', }); }, [optimisticActions, createProduct]); } export function useDeleteProducts(optimisticActions: CollectionOptimisticActions) { const { deleteProduct } = productsV3; return useCallback((productsToDelete: productsV3.V3Product[]) => { optimisticActions.deleteMany(productsToDelete, { submit: async (deletedProducts: productsV3.V3Product[]) => ( await Promise.all( deletedProducts.map((product) => deleteProduct(product._id!)) ) ), successToast: { message: `${ productsToDelete.length > 1 ? 'Products' : 'Product' } deleted successfully`, type: 'SUCCESS', }, errorToast: () => `Failed to delete ${ productsToDelete.length > 1 ? 'Products' : 'Product' }`, }); }, [optimisticActions, deleteProduct]); } ``` ## Step 9 | Create a "Create Product" modal We need a modal where the user can enter the name of the new product and confirm or cancel. This modal will be opened using a button on the dashboard page. To build this modal: 1. Create a new folder in your app's repo under `src > extensions > dashboard` named `components`. 1. Create a new file in `src > extensions > dashboard > components` named `create-product.tsx`. 1. Import: - `React`, `useEffect`, and `useState`. - The components of the Wix Design System required to create our modal. Learn more about the [`Modal` component](https://www.wix-pages.com/wix-design-system/?path=/story/components-overlays-modal--modal). ```js import React, { useEffect, useState } from 'react'; import { Modal, CustomModalLayout, FormField, Input, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; ``` 1. Create a `CreateProductModal` component to define the appearance and functionality of the modal. Use the following code: ```js export function CreateProductModal({ showModal, onSave }: { showModal: boolean, onSave: (name: string) => void }) { const [productName, setProductName] = useState(''); const [shown, setShown] = useState(showModal); useEffect(() => { setShown(showModal); }, [showModal]) const toggleModal = () => { setShown(!shown); setProductName(''); }; return ( { onSave(productName) setProductName('') }} secondaryButtonText="Cancel" secondaryButtonOnClick={toggleModal} onCloseButtonClick={toggleModal} content={ setProductName(e.currentTarget.value)} /> } /> ); } ``` Let’s break down the above code: - **Lines 2-3**: Initialize state variables to manage the product name and the visibility of the modal. - **Lines 5-7**: Use `useEffect()` to ensure that when the `showModal` prop updates, the `shown` state variable aligns with it. - **Lines 9-12**: Set up a function to toggle the visibility of the modal and reset the product name input whenever the modal is shown or hidden. - **Lines 15-20**: Configure the modal to close when it’s toggled or if the site owner clicks outside of it on the page. - **Lines 21-42**: Configure the modal’s layout and content. - **Lines 23-30**: Add a primary **Save** button that is enabled when a product name has been entered. When clicked, it creates a product, and then resets `productName`. - **Lines 31-32**: Add a secondary **Cancel** button. When clicked, it closes the modal. - **Lines 35-40**: Add a `FormField` component that takes an input for the product name. Your complete `create-product.tsx` file should look like this: ```js import React, { useEffect, useState } from 'react'; import { Modal, CustomModalLayout, FormField, Input, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; export function CreateProductModal({ showModal, onSave }: { showModal: boolean, onSave: (name: string) => void }) { const [productName, setProductName] = useState(''); const [shown, setShown] = useState(showModal); useEffect(() => { setShown(showModal); }, [showModal]) const toggleModal = () => { setShown(!shown); setProductName(''); }; return ( { onSave(productName) setProductName('') }} secondaryButtonText="Cancel" secondaryButtonOnClick={toggleModal} onCloseButtonClick={toggleModal} content={ setProductName(e.currentTarget.value)} /> } /> ); } ``` ## Step 10 | Create a dashboard page component Finally, we have all the pieces in place to set up our dashboard page. Open the `custom-products-catalog.tsx` file at `src/extensions/dashboard/pages/custom-products-catalog/`. 1. Delete all the contents - we're starting from scratch. 1. Add the following import statements: ```js import React, { useState } from 'react'; import { Box, Text, Image, Breadcrumbs } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import { productsV3 } from '@wix/stores'; import { CollectionPage } from '@wix/patterns/page'; import { useTableCollection, Table, PrimaryActions, useOptimisticActions, deleteSecondaryAction, MultiBulkActionToolbar, CustomColumns, CollectionToolbarFilters, dateRangeFilter, DateRangeFilter, } from '@wix/patterns'; import type { Filter, RangeItem } from '@wix/patterns'; ``` This file is where we start to use [Patterns](https://www.wix-pages.com/wix-patterns/). Patterns is a React library with advanced components that extend the functionality of the core UI React components from the [Wix Design System](https://www.wix-pages.com/wix-design-system/). It simplifies Wix app development by enabling you to easily and consistently implement complex functionalities like querying, displaying, and filtering collection data from remote servers. In our code, we use Patterns components to create a table of products that you can query, filter, and sort. The table also has functionality to create new products, delete existing products, and choose which columns to display. 1. Import everything we set up in the previous sections: ```js import { withProviders } from '../../withProviders'; import { useCreateProduct, useDeleteProducts } from '../../hooks/stores'; import { CreateProductModal } from '../../components/create-product'; ``` 1. Define a type for filtering the table's data: ```js type TableFilters = { updatedDate: Filter>; } ``` This is the product property by which the table can be filtered. We'll pass this type to the `useTableCollection` hook later. 1. Define a map from column IDs to the field paths that `searchProducts` accepts for sorting: ```js type ProductSearch = Parameters[0]; type SortableField = NonNullable[number]['fieldName']>; const sortFieldByColumnId: Record = { price: 'actualPriceRange.minValue.amount', }; ``` The `SortableField` type narrows the allowed values to the field paths that the V3 `searchProducts` endpoint accepts as sort fields. The map then translates between the table column's `id`, which we use as a short human-friendly slug, and the API's full field path. For example, the **Price** column has `id: 'price'`, but the SDK expects the full path `actualPriceRange.minValue.amount` when sorting by price. The map entry `price: 'actualPriceRange.minValue.amount'` makes the translation explicit. Columns whose `id` already equals a sortable field path don't need an entry. 1. Map product types to strings so that they display in a readable way in the table: ```js const productTypeToDisplayName: {[key in productsV3.ProductType]: string} = { [productsV3.ProductType.PHYSICAL]: 'Physical', [productsV3.ProductType.DIGITAL]: 'Digital', } ``` ### Create the `Products()` component It's finally time to make our dashboard page component. We will: - [Define and export our dashboard page component](#define-and-export-our-dashboard-page-component) - [Define the component's states, hooks, and functions](#define-the-components-states-hooks-and-functions) - [Create the page structure](#create-the-page-structure) #### Define and export our dashboard page component 1. Create a function for our dashboard page component named `Products`: ```js function Products() {} ``` 1. Export the component wrapped by `withProviders()`: ```js export default withProviders(Products); ``` The rest of the code on this page will be written inside the `Products()` function. #### Define the component's states, hooks, and functions 1. Initialize the `shown` state variable to manage the visibility of the modal we created [earlier](#step-8--create-a-create-product-modal). ```js const [shown, setShown] = useState(false); ``` 1. Use `productsV3` to get the `searchProducts()` and `deleteProduct()` functions. ```js const { searchProducts, deleteProduct } = productsV3; ``` 1. Create the [`tableState`](https://www.wix-pages.com/wix-patterns/?path=/story/base-components-collections-table-tablestate--tablestate) using [`useTableCollection`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-collections-table-usetablecollection--usetablecollection). `useTableCollection` is a hook that returns a `tableState` object. This is where we define: - Where to retrieve the data from to display in the table. - How much data to retrieve. - How to build the search request that retrieves the table data, depending on what filters and sorting are applied in the table. - The error message to display if no data is found. - How the filters are applied. ```js const tableState = useTableCollection({ queryName: 'products-catalog', itemKey: (product: productsV3.V3Product) => product._id!, itemName: (product: productsV3.V3Product) => product.name!, limit: 20, fetchData: async (query) => { const { limit, sort, filters } = query; const filter: Record = {}; const updatedDate = filters?.updatedDate; if (updatedDate?.from || updatedDate?.to) { const conditions: Record[] = []; if (updatedDate.from) { conditions.push({ _updatedDate: { $gt: updatedDate.from.toISOString() } }); } if (updatedDate.to) { conditions.push({ _updatedDate: { $lt: updatedDate.to.toISOString() } }); } if (conditions.length === 1) { Object.assign(filter, conditions[0]); } else { filter['$and'] = conditions; } } const sortSpec = (sort ?? []) .map(s => { const fieldName = (sortFieldByColumnId[s.fieldName] ?? s.fieldName) as SortableField; return { fieldName, order: s.order === 'desc' ? 'DESC' as const : 'ASC' as const }; }); const response = await searchProducts( { cursorPaging: { limit }, ...(Object.keys(filter).length > 0 ? { filter } : {}), ...(sortSpec.length > 0 ? { sort: sortSpec } : {}), }, { fields: ['THUMBNAIL', 'PLAIN_DESCRIPTION'] } ); return { items: response.products ?? [], total: response.products?.length ?? 0, }; }, fetchErrorMessage: () => 'Error fetching products', filters: { updatedDate: dateRangeFilter(), }, }); ``` Let's break down the above code: - **Lines 2-5**: Define basic data necessary for retrieving and identifying the items used to populate the table. - **Lines 7-46**: Define the function that fetches the collection data used to populate the table. It receives a `query` object and returns the matching products plus a total count. - **Line 8**: Deconstruct the `query` parameter. It contains the requested `limit`, the active `sort`, and any `filters` the user has applied. - **Lines 10-25**: Build the filter object. If the user has applied a **Last Updated** range, translate it into the V3 search filter syntax. A range with both `from` and `to` becomes two separate `_updatedDate` conditions combined with `$and`, because the V3 search filter requires a single operator per field per condition. - **Lines 27-31**: Build the sort spec. For each sort entry from the table, look up the column's `id` in `sortFieldByColumnId` to get the field path the SDK accepts, and translate the order from the patterns library's lowercase format (`asc`/`desc`) into the SDK's uppercase enum (`ASC`/`DESC`). - **Lines 33-40**: Call `searchProducts`. Pass the cursor paging limit, the filter, and the sort spec as the first argument. In the second argument, request the additional response fields `THUMBNAIL` and `PLAIN_DESCRIPTION`. These aren't returned by default, but the **Avatar** and **Description** columns need them. - **Lines 42-45**: Return the items. This sample fetches a single page of up to 20 products. V3 cursor-paginated responses don't include a total count, so we use the length of the returned page as `total`. A production app should implement cursor-based pagination using `pagingMetadata.cursors.next` from the response, and call `countProducts` to display a true total. - **Line 48**: Define the error message that will be shown if the fetch fails. - **Lines 50-52**: Define which filter factories are used with the filters. The `updatedDate` filter uses the [`dateRangeFilter`](https://wix-pages.com/wix-patterns/?path=%2Fstory%2Ffeatures-filter-factories--daterangefilter) factory. 1. Create the [`optimisticActions`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Ffeatures-actions-updates--collectionoptimisticactions) class using [`useOptimisticActions`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Ffeatures-actions-updates--useoptimisticactions). `useOptimisticActions` is a hook that returns a `CollectionOptimisticActions` class. The code below defines logic that anticipates the results of the query that we defined in the previous step. When there is a change that sends a new query to the server (for example, a new filter), this logic will be applied to the data already in the page's memory while we're waiting for the server's response. ```js const optimisticActions = useOptimisticActions(tableState.collection, { orderBy: () => [], predicate: ({ filters }) => { return (product) => { if (filters.updatedDate && product._updatedDate) { const from = filters.updatedDate.from; const to = filters.updatedDate.to; const productUpdatedDate = (new Date(product._updatedDate)).getTime(); if (from && productUpdatedDate < from.getTime()) { return false; } if (to && productUpdatedDate > to.getTime()) { return false; } } return true; } }, } ); ``` Let's break down the above code: - **Line 3**: Define that there is no default sorting for the data, apart from what is defined in the query. - **Lines 5-22**: Define a function that simulates the filtering on the server, as defined in the `tableState` above. It returns a function that returns `true` if the product would pass the filters, and `false` if it wouldn't. - **Lines 7-18**: Return `false` if the product wouldn't pass the **Last Updated** filter. - **Line 20**: Return `true` if the product would pass all of the filters. 1. Define the `createProduct()` and `deleteProducts()` functions using the hooks we created in [Step 7](#step-7--create-hooks-for-our-sdk-calls): ```js const createProduct = useCreateProduct(optimisticActions); const deleteProducts = useDeleteProducts(optimisticActions); ``` At this stage, your `Products()` function should look like this: ```js function Products() { const [shown, setShown] = useState(false); const { searchProducts, deleteProduct } = productsV3; const tableState = useTableCollection({ queryName: 'products-catalog', itemKey: (product: productsV3.V3Product) => product._id!, itemName: (product: productsV3.V3Product) => product.name!, limit: 20, fetchData: async (query) => { const { limit, sort, filters } = query; const filter: Record = {}; const updatedDate = filters?.updatedDate; if (updatedDate?.from || updatedDate?.to) { const conditions: Record[] = []; if (updatedDate.from) { conditions.push({ _updatedDate: { $gt: updatedDate.from.toISOString() } }); } if (updatedDate.to) { conditions.push({ _updatedDate: { $lt: updatedDate.to.toISOString() } }); } if (conditions.length === 1) { Object.assign(filter, conditions[0]); } else { filter['$and'] = conditions; } } const sortSpec = (sort ?? []) .map(s => { const fieldName = (sortFieldByColumnId[s.fieldName] ?? s.fieldName) as SortableField; return { fieldName, order: s.order === 'desc' ? 'DESC' as const : 'ASC' as const }; }); const response = await searchProducts( { cursorPaging: { limit }, ...(Object.keys(filter).length > 0 ? { filter } : {}), ...(sortSpec.length > 0 ? { sort: sortSpec } : {}), }, { fields: ['THUMBNAIL', 'PLAIN_DESCRIPTION'] } ); return { items: response.products ?? [], total: response.products?.length ?? 0, }; }, fetchErrorMessage: () => 'Error fetching products', filters: { updatedDate: dateRangeFilter(), }, }); const optimisticActions = useOptimisticActions(tableState.collection, { orderBy: () => [], predicate: ({ filters }) => { return (product) => { if (filters.updatedDate && product._updatedDate) { const from = filters.updatedDate.from; const to = filters.updatedDate.to; const productUpdatedDate = (new Date(product._updatedDate)).getTime(); if (from && productUpdatedDate < from.getTime()) { return false; } if (to && productUpdatedDate > to.getTime()) { return false; } } return true; } }, } ); const createProduct = useCreateProduct(optimisticActions); const deleteProducts = useDeleteProducts(optimisticActions); return () } ``` #### Create the page structure We define our page as follows: 1. Define the [`CollectionPage`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-page-layouts-collection-page--collectionpage) component that will wrap all our components on this page: ```js // All page components will go here. ``` 1. Add a [`CollectionPage.Header`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-page-layouts-collection-page--collectionpage-header) component that contains the page title, breadcrumbs, and a button that opens our **Add Product** modal: ```js } primaryAction={ setShown(!shown)} /> } /> ``` 1. Add a [`CollectionPage.Content`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-page-layouts-collection-page--collectionpage-content) component below the `CollectionPage.Header`. This will wrap the rest of the components on this page. ```js // The rest of the page components will go here. ``` 1. Add the `CreateProductModal` component that we created in [Step 8](#step-8--create-a-create-product-modal): ```js { createProduct(productName); setShown(false); }}/> ``` 1. Add a [`Table`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-collections-table-table--table) component. The following code displays a table containing our columns and fetched data. When no items are selected, there is also a button that opens an accordion to customize the columns displayed, and a button that opens an accordion to filter the data. When at least one product is selected, the table displays the number of selected products, the maximum number of products that can be selected, a **Select All** option, and a **Delete** button. ```js } bulkActionToolbar={({ selectedValues, openConfirmModal }) => { const disabled = selectedValues.length > 20; return ( { openConfirmModal({ theme: 'destructive', primaryButtonOnClick: () => { deleteProducts(selectedValues); }, }); }, }, ]} />) }} customColumns={} columns={[ { id: 'avatar', name: 'Avatar', title: '', width: '72px', render: (product) => , reorderDisabled: true, hiddenFromCustomColumnsSelection: true }, { id: 'name', title: 'Product / Description', render: (row: productsV3.V3Product) => ( {row.name} {row.plainDescription} ), width: 'auto', reorderDisabled: true, hideable: false }, { id: 'price', title: 'Price', render: (row: productsV3.V3Product) => { const amount = row.actualPriceRange?.minValue?.amount; return amount ? `$${amount}` : ''; }, width: '100px', sortable: true, }, { id: 'type', title: 'Type', render: (row: productsV3.V3Product) => { if (!row.productType) return ''; return productTypeToDisplayName[row.productType as productsV3.ProductType] ?? row.productType; }, width: '100px', }, { id: 'last-updated', title: 'Last Updated', render: (row: productsV3.V3Product) => row._updatedDate ? new Date(row._updatedDate).toLocaleDateString() : '', width: '100px', defaultHidden: true, }, ]} actionCell={(_product, _index, actionCellAPI) => ({ secondaryActions: [ deleteSecondaryAction({ optimisticActions, actionCellAPI, submit: (products: productsV3.V3Product[]) => ( Promise.all( products.map((product: productsV3.V3Product) => deleteProduct(product._id!)) ) ), successToast: { message: `${_product.name} deleted successfully.`, type: 'SUCCESS', }, errorToast: () => 'Product deletion failed.', }), ] })} /> ``` Let's identify where some of our key functionality is implemented in the above code. For more information on all the `Table` props, see [Patterns Table Component](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Fbase-components-collections-table-table--table&tab=API). - **Lines 4-12**: Define the table's filters. We use a [`CollectionToolbarFilters`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Ffeatures-filter-components--toolbarfilters) component to display an accordion with the filtering options. The **Last Updated** filter uses the [`DateRangeFilter`](https://www.wix-pages.com/wix-patterns/?path=/story/features-filter-components--daterangefilter) component. - **Lines 13-35**: Use the [`MultiBulkActionToolbar`](https://www.wix-pages.com/wix-patterns/?path=%2Fstory%2Ffeatures-actions-bulk-actions--multibulkactiontoolbar) component to create a button in the toolbar that deletes all selected products. When the button is clicked, the user is asked to confirm their decision in a modal (**line 25**). - **Lines 37-94**: Define the table's columns. - **Lines 39-47**: **Product avatar** - Use the [`Image`](https://www.wix-pages.com/wix-design-system/?path=/story/components-media--image) component from the Wix Design System and feed it the URL from the product's `media.main.thumbnail.url` field. To make this field appear in the response, we requested it via the `THUMBNAIL` field in the `searchProducts` call. - **Lines 48-64**: **Product name and description** - Use a `Box` for layout with direction set to `vertical` and gap to `3px`, indicating vertical stacking with a gap of 3px. Inside this box, add two `Text` components for the product's `name` and `plainDescription`. The description comes from the `PLAIN_DESCRIPTION` requested field. - **Lines 65-74**: **Product price** - Read the product's price from `actualPriceRange.minValue.amount` and prefix it with a dollar sign. The column is `sortable: true`; sorting maps to the `actualPriceRange.minValue.amount` field path via `sortFieldByColumnId`. - **Lines 75-83**: **Product type** - Format the `productType` field using `productTypeToDisplayName` which we defined earlier. If there is no product type, return an empty string. - **Lines 84-93**: **Product last updated date** - Format the product's `_updatedDate` as a readable date. This column is `defaultHidden: true`, so the user must opt in via the columns control to see it. - **Lines 96-113**: Create a **Delete** action in the per-row action menu (**line 99**). Add success (**lines 107-110**) and error (**line 111**) toasts. Your page structure code should look like this: ```js } primaryAction={ setShown(!shown)} /> } /> { createProduct(productName); setShown(false); }}/>
} bulkActionToolbar={({ selectedValues, openConfirmModal }) => { const disabled = selectedValues.length > 20; return ( { openConfirmModal({ theme: 'destructive', primaryButtonOnClick: () => { deleteProducts(selectedValues); }, }); }, }, ]} />) }} customColumns={} columns={[ { id: 'avatar', name: 'Avatar', title: '', width: '72px', render: (product) => , reorderDisabled: true, hiddenFromCustomColumnsSelection: true }, { id: 'name', title: 'Product / Description', render: (row: productsV3.V3Product) => ( {row.name} {row.plainDescription} ), width: 'auto', reorderDisabled: true, hideable: false }, { id: 'price', title: 'Price', render: (row: productsV3.V3Product) => { const amount = row.actualPriceRange?.minValue?.amount; return amount ? `$${amount}` : ''; }, width: '100px', sortable: true, }, { id: 'type', title: 'Type', render: (row: productsV3.V3Product) => { if (!row.productType) return ''; return productTypeToDisplayName[row.productType as productsV3.ProductType] ?? row.productType; }, width: '100px', }, { id: 'last-updated', title: 'Last Updated', render: (row: productsV3.V3Product) => row._updatedDate ? new Date(row._updatedDate).toLocaleDateString() : '', width: '100px', defaultHidden: true, }, ]} actionCell={(_product, _index, actionCellAPI) => ({ secondaryActions: [ deleteSecondaryAction({ optimisticActions, actionCellAPI, submit: (products: productsV3.V3Product[]) => ( Promise.all( products.map((product: productsV3.V3Product) => deleteProduct(product._id!)) ) ), successToast: { message: `${_product.name} deleted successfully.`, type: 'SUCCESS', }, errorToast: () => 'Product deletion failed.', }), ] })} /> ``` #### Complete page code Your complete code should look like this: ```js import React, { useState } from 'react'; import { Box, Text, Image, Breadcrumbs } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import { productsV3 } from '@wix/stores'; import { CollectionPage } from '@wix/patterns/page'; import { useTableCollection, Table, PrimaryActions, useOptimisticActions, deleteSecondaryAction, MultiBulkActionToolbar, CustomColumns, CollectionToolbarFilters, dateRangeFilter, DateRangeFilter, } from '@wix/patterns'; import type { Filter, RangeItem } from '@wix/patterns'; import { withProviders } from '../../withProviders'; import { useCreateProduct, useDeleteProducts } from '../../hooks/stores'; import { CreateProductModal } from '../../components/create-product'; type TableFilters = { updatedDate: Filter>; } type ProductSearch = Parameters[0]; type SortableField = NonNullable[number]['fieldName']>; const sortFieldByColumnId: Record = { price: 'actualPriceRange.minValue.amount', }; const productTypeToDisplayName: {[key in productsV3.ProductType]: string} = { [productsV3.ProductType.PHYSICAL]: 'Physical', [productsV3.ProductType.DIGITAL]: 'Digital', } function Products() { const [shown, setShown] = useState(false); const { searchProducts, deleteProduct } = productsV3; const tableState = useTableCollection({ queryName: 'products-catalog', itemKey: (product: productsV3.V3Product) => product._id!, itemName: (product: productsV3.V3Product) => product.name!, limit: 20, fetchData: async (query) => { const { limit, sort, filters } = query; const filter: Record = {}; const updatedDate = filters?.updatedDate; if (updatedDate?.from || updatedDate?.to) { const conditions: Record[] = []; if (updatedDate.from) { conditions.push({ _updatedDate: { $gt: updatedDate.from.toISOString() } }); } if (updatedDate.to) { conditions.push({ _updatedDate: { $lt: updatedDate.to.toISOString() } }); } if (conditions.length === 1) { Object.assign(filter, conditions[0]); } else { filter['$and'] = conditions; } } const sortSpec = (sort ?? []) .map(s => { const fieldName = (sortFieldByColumnId[s.fieldName] ?? s.fieldName) as SortableField; return { fieldName, order: s.order === 'desc' ? 'DESC' as const : 'ASC' as const }; }); const response = await searchProducts( { cursorPaging: { limit }, ...(Object.keys(filter).length > 0 ? { filter } : {}), ...(sortSpec.length > 0 ? { sort: sortSpec } : {}), }, { fields: ['THUMBNAIL', 'PLAIN_DESCRIPTION'] } ); return { items: response.products ?? [], total: response.products?.length ?? 0, }; }, fetchErrorMessage: () => 'Error fetching products', filters: { updatedDate: dateRangeFilter(), }, }); const optimisticActions = useOptimisticActions(tableState.collection, { orderBy: () => [], predicate: ({ filters }) => { return (product) => { if (filters.updatedDate && product._updatedDate) { const from = filters.updatedDate.from; const to = filters.updatedDate.to; const productUpdatedDate = (new Date(product._updatedDate)).getTime(); if (from && productUpdatedDate < from.getTime()) { return false; } if (to && productUpdatedDate > to.getTime()) { return false; } } return true; } }, } ); const createProduct = useCreateProduct(optimisticActions); const deleteProducts = useDeleteProducts(optimisticActions); return ( } primaryAction={ setShown(!shown)} /> } /> { createProduct(productName); setShown(false); }}/>
} bulkActionToolbar={({ selectedValues, openConfirmModal }) => { const disabled = selectedValues.length > 20; return ( { openConfirmModal({ theme: 'destructive', primaryButtonOnClick: () => { deleteProducts(selectedValues); }, }); }, }, ]} />) }} customColumns={} columns={[ { id: 'avatar', name: 'Avatar', title: '', width: '72px', render: (product) => , reorderDisabled: true, hiddenFromCustomColumnsSelection: true }, { id: 'name', title: 'Product / Description', render: (row: productsV3.V3Product) => ( {row.name} {row.plainDescription} ), width: 'auto', reorderDisabled: true, hideable: false }, { id: 'price', title: 'Price', render: (row: productsV3.V3Product) => { const amount = row.actualPriceRange?.minValue?.amount; return amount ? `$${amount}` : ''; }, width: '100px', sortable: true, }, { id: 'type', title: 'Type', render: (row: productsV3.V3Product) => { if (!row.productType) return ''; return productTypeToDisplayName[row.productType as productsV3.ProductType] ?? row.productType; }, width: '100px', }, { id: 'last-updated', title: 'Last Updated', render: (row: productsV3.V3Product) => row._updatedDate ? new Date(row._updatedDate).toLocaleDateString() : '', width: '100px', defaultHidden: true, }, ]} actionCell={(_product, _index, actionCellAPI) => ({ secondaryActions: [ deleteSecondaryAction({ optimisticActions, actionCellAPI, submit: (products: productsV3.V3Product[]) => ( Promise.all( products.map((product: productsV3.V3Product) => deleteProduct(product._id!)) ) ), successToast: { message: `${_product.name} deleted successfully.`, type: 'SUCCESS', }, errorToast: () => 'Product deletion failed.', }), ] })} /> ); } export default withProviders(Products); ``` ## Step 11 | Test the app Now that the app’s code is ready, you can test it locally using the Wix CLI. To test your app, do the following: 1. Run a local development server for your app using the `wix dev` command in your terminal. Your app’s dashboard page will now look like this: ![App preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2d80442fb96a66bfe7f0c50010094c9f.png) 1. Click the **Add Product** button and use the modal to add a product. 1. Select some products and click the **Delete** button. 1. If things don’t look right, open your browser’s developer tools and check for errors in the console. ## Step 12 | Build and deploy the app You have now fully developed an app that allows users to add or remove products from a Wix Stores products catalog through a dashboard page. After testing your app and seeing that it works as expected, you can [build and deploy your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/project-development/build-and-deploy.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a “Top Blog Posts” Dashboard Page With the CLI ## Article: Tutorial | Create a “Top Blog Posts” Dashboard Page With the CLI ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-top-blog-posts-dashboard-page-with-the-cli.md ## Article Content: # Tutorial | Create a Top Blog Posts App Using the Wix CLI This tutorial demonstrates how to use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) to build a Top Blog Posts app on the [Wix Platform](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md). The app contains a dashboard page that shows the most-viewed and most-liked posts of a Wix site's blog in two cards. By following this tutorial, you'll learn how to: - Build a dashboard page using the [Wix Design System](https://www.wixdesignsystem.com/). - Query blog posts and retrieve per-post metrics using the [Wix Blog SDK](https://dev.wix.com/docs/api-reference/business-solutions/blog/introduction?apiView=SDK.md). - Navigate to a built-in Wix dashboard page from within your app, using the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md). The end result will look like this: ![App preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/eace32400be38d0fabb96420f0bb3177.png) We'll use the following steps to build the Top Blog Posts app: 1. [Initialize the app](#step-1--initialize-the-app). 1. [Create the dashboard page](#step-2--create-the-dashboard-page). 1. [Run a local development server](#step-3--run-a-local-development-server). 1. [Add permissions](#step-4--add-permissions). 1. [Set up Wix Blog](#step-5--set-up-wix-blog). 1. [Develop the app](#step-6--develop-the-app). 1. [Display the top posts in the dashboard](#step-7--display-the-top-posts-in-the-dashboard). 1. [Test the app](#step-8--test-the-app). 1. [Build and deploy the app](#step-9--build-and-deploy-the-app). ## Before you begin Before getting started, make sure that: - You install [Node.js](https://nodejs.org/en/download/) (v20.11.0 or higher). - You install [npm](https://www.npmjs.com/package/npm) or [yarn](https://www.npmjs.com/package/yarn). - You're logged into your Wix Studio account. If you don't already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). ## Step 1 | Initialize the app We use the Wix CLI to initialize our Top Blog Posts app. In the process of initializing our app, the Wix CLI automatically: - Creates a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps) of your Wix Studio workspace. - Sets up a new folder for your app in your local file system. The folder includes: - A `src` folder containing initial boilerplate code for an app with a dashboard page. - A `package.json` file containing your app dependencies. - Creates a local Git repository for your app. To initialize the app: 1. Open a terminal and navigate to the folder where you want to create your app. 2. Run the following command: ```bash npm create @wix/new@latest app ``` If prompted to install the `@wix/create-new` package, press `y`. 3. Select **Create a new Wix App**. 4. Select **Create a basic app**. 5. Enter a name for your app. Let’s name our app `Top Blog Posts`. 6. Press `Enter` to accept the default folder name (`top-blog-posts`). 7. When asked to configure Wix MCP for your IDE, select your preferred MCP. A green **Success** message confirms that your app has been registered in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 8. Press `Enter` to accept the default namespace (`top-blog-posts`). 9. Press `Enter` to accept the default code identifier (`top_blog_posts`). You now have a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps), a new folder in your local file system, and a local Git repository for developing and testing your app. ## Step 2 | Create the dashboard page We use the CLI's `generate` command to create a dashboard page for our app. To create the dashboard page: 1. Navigate to your newly created folder for your app. ```bash cd top-blog-posts ``` 2. Run the following command and follow the prompts: ```bash wix generate ``` 3. When prompted for the kind of extension, select **Dashboard Page**. 4. When prompted for a page title, enter **Top Blog Posts**. 5. When prompted for the page route, press `Enter` to accept the default (`top-blog-posts`). Upon completion, the extension files are created at `src/extensions/dashboard/pages/top-blog-posts/`: ```bash src └── extensions └── dashboard └── pages └── top-blog-posts ├── top-blog-posts.extension.ts └── top-blog-posts.tsx ``` For more information about these files, see [Dashboard Page Extension Files and Code](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/dashboard/dashboard-pages/dashboard-page-extension-files-and-code.md).
**Tip:** The CLI's default scaffold also created a `my-page` extension at `src/extensions/dashboard/pages/my-page/`. You can leave it (it won't interfere with this tutorial) or delete the `my-page/` folder to keep the dashboard sidebar showing only the **Top Blog Posts** page.
## Step 3 | Run a local development server Now that you’ve initialized your app, you can run a local development server to see the app in action, and view local changes as you develop your app. To run a local development server for your app: 1. In the terminal, from your app's folder, run the following command: ```bash wix dev ``` 1. The CLI prompts you to choose a development site (test site), which you’ll use throughout this tutorial to run and test your app. You can choose an existing Wix site as your development site, or create a new one. Let’s **Create a new Development Site**. The newly created development site is automatically named **Dev Sitex** followed by a number (for example, **Dev Sitex 12345**), and can be found in your Wix account’s list of sites. 1. Follow the prompt to open the app installation page in your default browser. If the browser doesn’t open, [install your app on your test site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md) manually and skip the next step. 1. Click **Agree & Add** to install your app on your development site. 1. In the terminal menu, select **Dashboard** to open your development site’s dashboard. 1. In the dashboard sidebar, click **Apps > Top Blog Posts** to see your newly created app’s dashboard page. We add the content of our app’s dashboard page in the next step. ![Dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/eb6fc85906e9be95e48eacc52ffdd8b6.png) Your app is now running on your development site. As you develop your app, any changes made to your app’s code are reflected in real time on your development site. If your changes don’t show up, try refreshing the page, or closing and reopening the development site. ## Step 4 | Add permissions In this step, we'll add permissions for the app. Every SDK API requires specific permissions to use. In this app, we will use the [`queryPosts()`](https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/query-posts.md) and [`getPostMetrics()`](https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/get-post-metrics.md) functions in the [Wix Blog API](https://dev.wix.com/docs/api-reference/business-solutions/blog/introduction?apiView=SDK.md) to get a list of posts and retrieve their metrics. To use these functions, we need to give our app permission requirements in the app dashboard. Once we do this, anyone installing the app will be prompted to grant the specified permissions. Both `queryPosts()` and `getPostMetrics()` require the same scope: a read blog scope. Each method's reference page (linked above) lists the exact scope it requires in its **Permission Scopes** section, including the scope's ID (`SCOPE.DC-BLOG.READ-BLOGS`). To add the scope: 1. Go to the [**Permissions** tab](https://manage.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fdev-center-permissions) in your app's dashboard. 1. Click **Add Permissions**. 1. Search for the scope. Several scopes can have similar names, so match the one whose ID matches the scope ID shown on the method's reference page. 1. Check its checkbox under **Choose Permission Scopes** and click **Save**. For more information on configuring permissions, see [Configure permissions for your app](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md). ## Step 5 | Set up Wix Blog Before you can start coding, you need to set up Wix Blog on your site and app: 1. Install the [Wix Blog](https://www.wix.com/app-market/wix-blog?searchLocation=home) app on your test site. You can create some new blog posts, or use the sample ones provided by Wix. 1. Publish the site and “like” one of the posts. You can find the blog at `https://{your-site-url}/blog`. 1. Open your app’s dashboard in the [Custom Apps page](https://manage.wix.com/account/custom-apps). 1. Click **Test App**, then select **Test on dev site**. In the **Select a development site** dialog, choose your dev site and click **Test App**. The app is reinstalled on your dev site with the latest permissions. ## Step 6 | Develop the app Next, we will use the JavaScript SDK to retrieve blog data from the test site. ### Create a code file for your SDK code Before starting to write code for the SDK, set up a file for your code. The code for this project uses the SDK's [`blog`](https://dev.wix.com/docs/api-reference/business-solutions/blog/introduction?apiView=SDK.md) and [`dashboard`](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) modules. The `dashboard` module is preinstalled by the CLI, but you'll need to install the `blog` module manually. 1. Create a file named `blog-utils.js` at `src/extensions/dashboard/pages/top-blog-posts/`, alongside the `top-blog-posts.tsx` file you generated in Step 2. 1. In your IDE terminal, run the following command: ```bash npm install @wix/blog ``` ### Retrieve blog data Next, use the blog module in the SDK to retrieve the data you need. 1. Import the `posts` submodule from the `@wix/blog` package into your file. 1. Create an `async` function called `getTopBlogPosts`. 1. Use the [`queryPosts()`](https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/query-posts.md) function to find the posts with the most likes and views. The query allows you to sort the posts in descending order based on likes and views. Call this function twice, once for likes and once for views. 1. Extract the first item IDs from the result and save them as `mostLiked` and `mostViewed` variables. 1. Use the [`getPostMetrics()`](https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/get-post-metrics.md) function to retrieve the post metrics by passing the ID of the post. These metrics include likes and views. Save these metrics as `mostLikedMetrics` and `mostViewedMetrics` variables. 1. Your function should return an object with two keys, `mostViewed` and `mostLiked`. The value for each key should be an object containing the post data and the likes and views respectively of the highest-ranking posts. Your final `blog-utils.js` file should look like this: ```js import { posts } from '@wix/blog'; export async function getTopBlogPosts() { const mostLikedPosts = await posts.queryPosts().descending('metrics.likes').find(); const mostViewedPosts = await posts.queryPosts().descending('metrics.views').find(); const mostLiked = mostLikedPosts.items[0]; const mostViewed = mostViewedPosts.items[0]; const mostLikedMetrics = await posts.getPostMetrics(mostLiked._id); const mostViewedMetrics = await posts.getPostMetrics(mostViewed._id) return { mostViewed: { post: mostViewed, views: mostViewedMetrics.metrics.views ?? 0, }, mostLiked: { post: mostLiked, likes: mostLikedMetrics.metrics.likes ?? 0, }, }; } ``` ## Step 7 | Display the top posts in the dashboard Now that we have the blog data, we can use the [Wix Design System](https://www.wix-pages.com/wix-design-system/?path=/story/getting-started--about) to display it on a dashboard page. The app framework created by the CLI includes the code for a sample dashboard page. We can use that code as the basis for a dashboard page. ### Set up the dashboard code files 1. Open the `top-blog-posts.tsx` file at `src/extensions/dashboard/pages/top-blog-posts/`. 1. Delete all the import statements and paste in the following ones: ```tsx import { Button, Card, Heading, Text, Page, WixDesignSystemProvider, Box, Loader } from "@wix/design-system"; import "@wix/design-system/styles.global.css"; import * as Icons from "@wix/wix-ui-icons-common"; import React, { useEffect, useState } from "react"; import { dashboard } from "@wix/dashboard"; import { getTopBlogPosts } from './blog-utils'; import { posts } from '@wix/blog'; ``` 1. In the return statement for the `Index()` component, delete everything in between the `` tags. When you’re done, your file should look like this: ```tsx import { Button, Card, Heading, Text, Page, WixDesignSystemProvider, Box, Loader, } from "@wix/design-system"; import "@wix/design-system/styles.global.css"; import * as Icons from "@wix/wix-ui-icons-common"; import React, { useEffect, useState } from "react"; import { dashboard } from "@wix/dashboard"; import { getTopBlogPosts } from './blog-utils'; import { posts } from '@wix/blog'; export default function Index() { return ; } ``` ### Prepare blog data for display Next, get your blog data ready to display on your dashboard page. This code goes below the import statements on your `my-page.tsx` file: 1. Define the interfaces for viewed and liked blog posts. ```js interface ViewedBlogPost { post: posts.Post; views: number; } interface LikedBlogPost { post: posts.Post; likes: number; } ``` 1. In the `Index()` component use React hooks to access the data. ```ts const [mostViewed, setMostViewed] = useState(null); const [mostLiked, setMostLiked] = useState(null); const [mostViewedPublishedDate, setMostViewedPublishedDate] = useState(null); const [mostLikedPublishedDate, setMostLikedPublishedDate] = useState(null); useEffect(() => { async function fetchBlogPosts() { try { const { mostViewed, mostLiked } = await getTopBlogPosts(); setMostViewed(mostViewed); setMostLiked(mostLiked); setMostViewedPublishedDate( mostViewed.post.lastPublishedDate ? new Date(mostViewed.post.lastPublishedDate).toDateString() : null ); setMostLikedPublishedDate( mostLiked.post.lastPublishedDate ? new Date(mostLiked.post.lastPublishedDate).toDateString() : null ); } catch (error) { console.log("Failed to fetch blog post data.", error); } } fetchBlogPosts(); }, []); ``` 1. Add a loader that displays while data are loading. ```js if (!mostViewed || !mostLiked) { return ( ); } ``` ### Create the page structure Next, add React components from the [Wix Design System](https://www.wix-pages.com/wix-design-system/?path=/story/getting-started--about) to create the page structure. 1. Add nested components between the `` tags in the return statement for the `Index()` component. Structure your components to match the following diagram. Wrap `` components in `
` tags so that they each display on a different line. ![Outline boxes](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b4c306af7e075a32ab470c50c2e10c6e.png) 1. Add the following variables to the opening tag of the `Box` component to ensure the cards inside it display correctly: ```js gap="5px" direction="vertical" ``` When you’re done, your `Index()` component should look like this: ```js export default function Index() { return (
); } ``` ### Add content to the page Add static text, dynamic text, and other content to your Design System components. In some cases, text is added between the component’s opening and closing tags. In other cases, the text is passed into the component as a variable. 1. Pass these variables to the `Heading` components inside the `Card.Header` components: ```js size="medium" as="h2" ``` 1. Pass these variables to the `Heading` components inside the `Car.Content` components: ```js size="small" as="h3" ``` 1. Add an `onClick` event handler to the `Button` component in the page header actions bar. Set the variable to this function: ```js () => dashboard.navigate('social-blog'). ``` When you’re done, your `Index()` function should look like this: ```js export default function Index() { return ( } onClick={() => dashboard.navigate("social-blog")} > Edit Your Blog } /> Most Viewed } /> Title: {mostViewed.post.title} Excerpt: {mostViewed.post.excerpt}
Last published: {mostViewedPublishedDate}
Views: {mostViewed.views}
Most Liked } /> Title: {mostLiked.post.title} Excerpt: {mostLiked.post.excerpt}
Last published: {mostLikedPublishedDate}
Likes: {mostLiked.likes}
); } ``` ## Step 8 | Test the app Now that the app’s code is ready, you can test it locally using the Wix CLI. The finished code sample should look like this: ```ts import { Button, Card, Heading, Text, Page, WixDesignSystemProvider, Box, Loader, } from "@wix/design-system"; import "@wix/design-system/styles.global.css"; import * as Icons from "@wix/wix-ui-icons-common"; import React, { useEffect, useState } from "react"; import { dashboard } from "@wix/dashboard"; import { getTopBlogPosts } from './blog-utils'; import { posts } from '@wix/blog'; interface ViewedBlogPost { post: posts.Post; views: number; } interface LikedBlogPost { post: posts.Post; likes: number; } export default function Index() { const [mostViewed, setMostViewed] = useState(null); const [mostLiked, setMostLiked] = useState(null); const [mostViewedPublishedDate, setMostViewedPublishedDate] = useState(null); const [mostLikedPublishedDate, setMostLikedPublishedDate] = useState(null); useEffect(() => { async function fetchBlogPosts() { try { const { mostViewed, mostLiked } = await getTopBlogPosts(); setMostViewed(mostViewed); setMostLiked(mostLiked); setMostViewedPublishedDate( mostViewed.post.lastPublishedDate ? new Date(mostViewed.post.lastPublishedDate).toDateString() : null ); setMostLikedPublishedDate( mostLiked.post.lastPublishedDate ? new Date(mostLiked.post.lastPublishedDate).toDateString() : null ); } catch (error) { console.log("Failed to fetch blog post data.", error); } } fetchBlogPosts(); }, []); if (!mostViewed || !mostLiked) { return ( ); } return ( } onClick={() => dashboard.navigate("social-blog")} > Edit Your Blog } /> Most Viewed } /> Title: {mostViewed.post.title} Excerpt: {mostViewed.post.excerpt}
Last published: {mostViewedPublishedDate}
Views: {mostViewed.views}
Most Liked } /> Title: {mostLiked.post.title} Excerpt: {mostLiked.post.excerpt}
Last published: {mostLikedPublishedDate}
Likes: {mostLiked.likes}
); } ``` To test your app, do the following: 1. Run a local development server for your app. Your app’s dashboard page will now look like this: ![App preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/eace32400be38d0fabb96420f0bb3177.png) 1. Click the button to show top blog posts. 1. If things don’t look right, open your browser’s developer tools and check for errors in the console. ## Step 9 | Build and deploy the app After testing your app and seeing that it works as expected, you can: - [Create a public preview of your app](https://dev.wix.com/docs/build-apps/manage-your-app/versioning/about-app-versioning.md), - [Build your app](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/about-app-distribution.md) - [Deploy your app](https://dev.wix.com/docs/build-apps/manage-your-app/versioning/release-a-new-app-version.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Adjust a CLI App to Different Pricing Plans ## Article: Adjust a CLI App to Different Pricing Plans ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-adjust-a-cli-app-to-different-pricing-plans.md ## Article Content: # Tutorial | Adjust a CLI App to Different Pricing Plans
**Deprecated** The Wix CLI for Apps is deprecated and no longer receives updates or new features. New projects should use the unified [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md). [Determine which CLI your project uses](https://dev.wix.com/docs/wix-cli/guides/development/determine-which-cli-your-project-uses.md).
When you publish your CLI app to the Wix App Market, you can provide Wix users with different pricing plans. For example, you can provide a free version of your app and enable users to purchase a premium version with additional features. If you provide different plans for your app, it’s your responsibility as the developer to code behavior that limits features for certain plans. Your app should also provide calls-to-action to upgrade to paid plans where applicable. As an example, think of an app that provides Wix users with a site widget that they can place in their online store. When site visitors click on the widget, it randomly generates a discount that the visitor can use when purchasing products. The app also provides a dashboard page where Wix users can adjust the range of discount amounts that can be generated. By default, the app sets a range of 1-5%. Wix users who purchase a paid plan can alter this range. The end result looks like this: ![Discount generator example widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b39abaf836907eaa106b83385cd6d131.gif) This tutorial focuses on the dashboard code, which provides restrictive logic that prevents free plan Wix users from changing the range while allowing paid plan Wix users to do so. This tutorial uses the following steps to adjust a CLI app to different pricing plans: 1. Use the app instance to determine the plan. 2. Restrict features based on Wix user plan. 3. Include CTAs to upgrade. ## Before you begin - Create an app in the app dashboard. - Select a [business model](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md) for your app that involves multiple plans, for example a freemium model with free and paid plans. ## Step 1 | Determine the plan using the app instance This step shows how to retrieve the app instance information to determine the Wix user's current pricing plan. At the end of this step, you'll have the instance ID and plan information needed to apply feature restrictions. In order to know which app features to restrict, you need to know the user’s current plan. You can determine the user’s plan from the [app instance](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances?apiView=SDK.md). Fetch the app instance using the [`getAppInstance()`](https://dev.wix.com/docs/api-reference/app-management/app-instance/get-app-instance.md) method. The method returns an object containing information about the instance and the site it’s installed on. You can call `getAppInstance()` directly in your dashboard page code. From the returned object, you can extract several pieces of information: - The app instance ID. - The `isFree` parameter, which indicates if the user is on a free plan or not. - The `packageName` parameter, which tells you the plan the user selected. Here’s some sample code that extracts the instance ID and `isFree`: ```javascript import { appInstances } from '@wix/app-management'; const Index: FC = () => {     const [instanceId, setInstanceId] = useState(null);     const [isFree, setIsFree] = useState(true);       useEffect(() => {      const initializeInstance = async () => {       try {         const response = await appInstances.getAppInstance();         // Extract the instance ID and free status from the response         const instanceId = response.instance?.instanceId;         const isFreeStatus = response.instance?.isFree ?? true;                  setInstanceId(instanceId);         setIsFree(isFreeStatus);       } catch (error) { console.error('Failed to get app instance.', error);       } finally {         setIsLoading(false);       }     };     initializeInstance();   }, []); } ``` Once you know which plan an app instance uses, you can decide where to apply restrictive logic in your app. ## Step 2 | Restrict features based on Wix user plan This step demonstrates how to use the plan information to enable or disable app features. At the end of this step, you'll have implemented feature restrictions that differentiate between free and paid plan Wix users. If you offer a free plan and a single paid plan with your app, you can apply the `isFree` parameter to limit access to certain app features. The discount generator example sets a default range of percent discounts that can be randomly generated. We only want to allow paid plan users to alter the discount range. One way to achieve this behavior is to disable the fields that define discount ranges for free plan users, and enable them for paid plans. If you’re building a React component, you can use Wix design system elements to do this easily. Input elements such as [NumberInput](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--numberinput) let you enable or disable a field using the `disabled` boolean. In the code below, we directly use the `isFree` state to disable the input fields and save button. If a user is using a paid plan, `isFree` is false and the elements are enabled, allowing the user to alter the discount range. In this way, we restrict the ability to change the range to paid users. ```javascript   %}     onChange={(value) => handleInputChange('min', value || 0)}     defaultValue="1"     disabled={isFree}   />   %}     onChange={(value) => handleInputChange('max', value || 0)}     defaultValue="5"     disabled={isFree}   /> ``` If you offer multiple paid plans, use the `packageName` parameter to create behavior specific to each plan. ## Step 3 | Include CTAs to upgrade This step shows how to add upgrade prompts for Wix users on free plans. At the end of this step, you'll have clear calls to action that encourage Wix users to upgrade to paid plans. When you limit certain features by plan, notify the Wix user and include an option for them to upgrade. The call-to-action to upgrade can take any form that suits your app’s UI. In our discount generator example, we include a text button that links to the pricing plan page. ![Discount generator example dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8d95a68412682b28b88d0ead5ce9d5cc.png) The CTA appears conditionally, if the `isFree` is true: ```javascript     Upgrade to our premium plan to change the discount range.            Upgrade         ) : undefined} /> ``` ## Complete code Below is the complete dashboard page code for this example: ```javascript import React, { type FC, useState, useEffect } from 'react'; import { dashboard } from '@wix/dashboard'; import {   Button,   Card,   FormField,   Input,   NumberInput,   Page,   Loader,   WixDesignSystemProvider,   MessageBoxFunctionalLayout,   TextButton,   Box, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; import * as Icons from '@wix/wix-ui-icons-common'; import { appInstances } from '@wix/app-management'; import { members } from '@wix/members'; interface FormData {   min: number;   max: number; } const Index: FC = () => {   const [isLoading, setIsLoading] = useState(true);   const [instanceId, setInstanceId] = useState(null);   const [isFree, setIsFree] = useState(true);   const [formDataMap, setFormDataMap] = useState>(new Map());   const [currentFormData, setCurrentFormData] = useState({ min: 1, max: 5 });   const [isSaving, setIsSaving] = useState(false);   const [error, setError] = useState(null);   // Initialize on component mount   useEffect(() => {     const initializeInstance = async () => {       try {         setIsLoading(true);         const response = await appInstances.getAppInstance();         // Extract the instance ID and free status from the response         const instanceId = response.instance?.instanceId;         const isFreeStatus = response.instance?.isFree ?? true;                  setInstanceId(instanceId);         setIsFree(isFreeStatus);         // Load any existing form data for this instance         await loadFormData(instanceId);       } catch (error) { console.error('Failed to get app instance.', error);       } finally {         setIsLoading(false);       }     };     initializeInstance();   }, []);   // Load existing form data for this instance. // Local storage is used here for demo purposes.   // In production, save to your own backend with proper   // authentication and database storage.   const loadFormData = async (instanceId: string) => {     try {       const dataKey = `formData_${instanceId}`;       const savedData = localStorage.getItem(dataKey);       if (savedData) {         const parsedData = JSON.parse(savedData);         const formData = {           min: parsedData.min || 1,           max: parsedData.max || 5         };                  // Update the map and current form data         setFormDataMap(prev => new Map(prev).set(instanceId, formData));         setCurrentFormData(formData);                  dashboard.showToast({           message: 'Previous form data loaded',           type: 'standard'         });       }     } catch (error) {       console.error('Failed to load form data:', error);     }   };   // Save form data per app instance   const saveFormData = async () => {     if (!instanceId) return;     try {       setIsSaving(true);              // Update the map with current form data       setFormDataMap(prev => new Map(prev).set(instanceId, currentFormData));              // Local storage is used here for demo purposes.       // In production, save to your own backend with       // proper authentication and database storage.       const dataKey = `formData_${instanceId}`;       const dataToSave = {         ...currentFormData,         instanceId: instanceId,         timestamp: new Date().toISOString()       };              localStorage.setItem(dataKey, JSON.stringify(dataToSave));              dashboard.showToast({         message: 'Form data saved successfully!',         type: 'success'       });     } catch (error) {       console.error('Failed to save form data:', error);       dashboard.showToast({         message: 'Failed to save form data',         type: 'error'       });     } finally {       setIsSaving(false);     }   };   const handleInputChange = (field: keyof FormData, value: number) => {     setCurrentFormData(prev => ({ ...prev, [field]: value }));   };   return (                                                                                          Upgrade to our premium plan to change the discount range.                                            Upgrade                                                         ) : undefined}               />                                                                                       %}                       onChange={(value) => handleInputChange('min', value || 0)}                       defaultValue="1"                       disabled={isFree}                     />                                                                              %}                       onChange={(value) => handleInputChange('max', value || 0)}                       defaultValue="5"                       disabled={isFree}                     />                                                                                                                      ); }; export default Index; ``` ## See also - [About pricing plans and business models](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md) - [About app instances](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Site Plugin for the Wix Stores Product Page with the CLI ## Article: Tutorial | Create a Site Plugin for the Wix Stores Product Page with the CLI ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-site-plugin-for-the-wix-stores-product-page-with-the-cli.md ## Article Content: # Tutorial | Create a Site Plugin for the Wix Stores Product Page with the CLI In this tutorial, you'll create a digital sale banner [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Wix Stores product page using the [CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md). This plugin displays a small banner on digital products to let customers know this product is part of the digital sale promotion. The plugin appears in a Wix Stores [product page slot](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md#plugin-slots). Follow these steps to build the product page site plugin: 1. Create an app with the CLI. 2. Add a site plugin extension. 3. Code your plugin. 4. Test your plugin. 5. Build and deploy your app. > **Note:** Slots differ slightly depending on [which version of Wix Stores](https://support.wix.com/en/article/wix-stores-customizing-your-product-page) a user has on their site. This tutorial works for both versions. Learn more about building [product page site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md). ## Step 1 | Create an app with the CLI Create a new app project using the CLI. For detailed instructions, see [Quick Start an App](https://dev.wix.com/docs/wix-cli/guides/get-started/quick-start-an-app.md). ## Step 2 | Add a site plugin extension [Add a site plugin extension](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-plugins/add-a-site-plugin-extension.md) to your app that renders on a Wix Stores product page. To add a site plugin extension, follow these steps: 1. Run the following command: ```bash npm run generate ``` 2. When prompted, make sure to select the following: - `Site Plugin` as the extension. - `Wix Stores` as the Wix app. - `product-page-details-2` as the slot under `New product page`. 3. Open your plugin's `extension.ts` file and add support for the [old product page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md) slot so your plugin works with both versions of Wix Stores. The old product page uses the same `slotId` but has different `appDefinitionId` and `widgetId` values. Add the following placement to the `placements` array: ```tsx placements: [ { //New product page appDefinitionId: "a0c68605-c2e7-4c8d-9ea1-767f9770e087", widgetId: "6a25b678-53ec-4b37-a190-65fcd1ca1a63", slotId: "product-page-details-2" }, { //Old product page appDefinitionId: "1380b703-ce81-ff05-f115-39571d94dfcd", widgetId: "13a94f09-2766-3c40-4a32-8edb5acdd8bc", slotId: "product-page-details-2" } ] ``` ## Step 3 | Code your plugin Add code to check if a product is digital and then display a sale banner on digital products. If it's not a digital product, don't display any banner.
**Important:** You must support both [Stores V1 APIs](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/get-product.md) and [Stores V3 APIs](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/get-product.md) because different sites may have different API versions available. Your plugin should try both APIs and use whichever one works.
To code your plugin: 1. Open `src/site/plugins/custom-elements/digital-sale-banner/plugin.tsx`. 2. Delete the existing code in the file. 3. Paste the following import statements at the beginning of your file: > **Note:** Import both [Products V1 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/get-product.md) and [Products V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/get-product.md). ```tsx import { products, productsV3 } from '@wix/stores'; ``` 4. Create the custom element class. Define the class that extends `HTMLElement` and set up the observed attributes: ```tsx class SaleBanner extends HTMLElement { static get observedAttributes() { return ['display-name', 'product-id']; } connectedCallback() { this.checkProductAndRender(); } attributeChangedCallback() { this.checkProductAndRender(); } ``` 5. Add the method that checks if the product is digital. This method tries both the [Products V1 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/get-product.md) and [Products V3 API](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/get-product.md) and uses whichever one works: ```tsx async checkProductAndRender() { const productId = this.getAttribute('product-id'); if (!productId) { this.innerHTML = ''; return; } let isDigital = false; let foundProduct = false; // Try V1 API try { const response = await products.getProduct(productId); if (response.product) { foundProduct = true; if (response.product.productType === 'digital') { isDigital = true; } } } catch (error) { // V1 API failed, will try V3 } // If V1 didn't work, try V3 API if (!foundProduct) { try { const response = await productsV3.getProduct(productId); if (response) { foundProduct = true; if (response.productType === 'DIGITAL') { isDigital = true; } } } catch (error) { // Both APIs failed } } // Only show banner for digital products if (isDigital) { this.render(); } else { this.innerHTML = ''; } } ``` 6. Add the render method that displays the banner on the site: ```tsx render() { const customText = this.getAttribute('display-name'); const displayText = customText || 'Digital Sale'; this.innerHTML = `
${displayText}
`; } ``` 7. Export the class as the default export: ```tsx export default SaleBanner; ``` ### Complete example code ```tsx import { products, productsV3 } from '@wix/stores'; class SaleBanner extends HTMLElement { static get observedAttributes() { return ['display-name', 'product-id']; } connectedCallback() { this.checkProductAndRender(); } attributeChangedCallback() { this.checkProductAndRender(); } async checkProductAndRender() { const productId = this.getAttribute('product-id'); if (!productId) { this.innerHTML = ''; return; } let isDigital = false; let foundProduct = false; // Try V1 API try { const response = await products.getProduct(productId); if (response.product) { foundProduct = true; if (response.product.productType === 'digital') { isDigital = true; } } } catch (error) { // V1 API failed, will try V3 } // If V1 didn't work, try V3 API if (!foundProduct) { try { const response = await productsV3.getProduct(productId); if (response) { foundProduct = true; if (response.productType === 'DIGITAL') { isDigital = true; } } } catch (error) { // Both APIs failed } } // Only show banner for digital products if (isDigital) { this.render(); } else { this.innerHTML = ''; } } render() { const customText = this.getAttribute('display-name'); const displayText = customText || 'Digital Sale'; this.innerHTML = `
${displayText}
`; } } export default SaleBanner; ``` ## Step 4 | Test your plugin When you finish developing your plugin, test it in the development environment. To test your app, use the following steps: 1. Start the development server: ```bash wix dev ``` 2. Select **Editor** to open an editor in a local development environment. 3. Navigate to **Store Pages > Product Page** in the editor and click on the page to select it. 4. Click the **Plugin** icon that appears in the page toolbar. 5. In the Plugins modal, find the plugin you created and click **+ Add**. Your plugin should appear in the **product-page-details-2** slot. ## Step 5 | Build and deploy your app Build and deploy your app. For detailed instructions, see [Build and Deploy a Project](https://dev.wix.com/docs/wix-cli/guides/development/build-and-deploy-a-project.md). ## See also - [Add a Site Plugin Extension in the CLI](https://dev.wix.com/docs/wix-cli/legacy-clis/legacy-wix-cli-for-apps/supported-extensions/site-extensions/site-plugins/add-a-site-plugin-extension-in-the-cli.md) - [Site Plugin Extension Files and Code](https://dev.wix.com/docs/wix-cli/legacy-clis/legacy-wix-cli-for-apps/supported-extensions/site-extensions/site-plugins/site-plugin-extension-files-and-code.md) - [Wix Stores: Product Page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Counter Widget with Blocks ## Article: Creating a Counter Widget ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-counter-widget-with-blocks.md ## Article Content: # Tutorial | Create a Counter Widget Using Blocks
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
This tutorial shows you how to build a counter widget, where you can increment and decrement a number. Through this simple widget we will walk through everything that you can do with Wix Blocks.  ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/2714292d-944d-49c9-951a-83cc5c333e4b/95962bc8-fba5-4c08-b062-876165b5ca5a.gif) We recommend that you build this widget from scratch, but if you get stuck, you can [view a template of the widget in Blocks and edit it](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=bac5efe0-e532-4f58-8029-58adca2fb0b4&http_referrer=documentation) under your Wix account. ## Step 1 | Create a new Widget in Blocks [Create an empty Blocks app](https://support.wix.com/en/article/wix-blocks-creating-an-app-and-opening-it). Start with a blank canvas.   * * * ## Step 2 | Add a Flexbox A flexbox is a container that allows responsiveness. It will keep the other elements organized. To add a flexbox:  1. Click the **Add Elements** **+** button in the top menu.  2. Click **Layout ->**  **Flexboxes** 3. Click the vertical 3-section flexbox. 
Show screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/05/02/4bdd5662-aa86-4936-98a3-1189be629bb0/c789550f-3ad5-42a1-9f5d-d56735202358.png)
* * * ## Step 3 | Add Widget Elements ### Adding the buttons 1. Click the **Add Elements** **+** button on the top menu.  2. Click **Buttons**.  3. Drag and drop a Primary Button onto the right section of the flexbox.
Show screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/1b75c84b-580c-44c3-b24d-b0184518d29c/5f2a673b-964a-4ad5-aaa9-daf8e24aa166.png)
### Changing the text 1\.  Click **Settings**.  2\.  Select **Text only** from the drop down list.  3\.  Change the button's text or icon to "+". 4\.  Click the **Design** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/12/caba4f61-8f8f-4889-a38d-cbd18d0b8f63/a5d2a21c-5e1d-4636-9cd3-a964c0fdd520.png)  icon in the **Inspector** panel ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/05/14/abd2dd03-8a0b-49bd-8cb9-af4503103bb6/28a66f5a-598c-4d9d-86ab-35062a41c17b.png)  and select **Text**. 5\.  Click **Themes** and select **Heading 6** from the drop down list.  6\. Drag and drop another square button to the left section.  7\. Change the text to "-".
Show screenshot ![text menu](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ce1abc54635caea0f4e8055eea5d6429.png)
### Adding the middle number 1. Click the **Add Elements** **+** button and select **Text**. 2. Drag and drop an **32px Title** element to the central section of the flexbox.  3. Edit the text to "0" and align it to the center. 
Show screenshot ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/da3b13de4994964e8a1ec08df50fa834.png)
#### Changing the IDs 1. Click the increment button. 1. Click the **Properties and Events** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/10/25/b8b2d885-6d3d-45ce-b4d1-ad335143e391/c4cb59f0-340d-46ed-989c-2f1f56b3cae3.jpg)  icon in the bottom right of the screen.   2. Change the ID of your increment button to **incBtn**. 3. Change the ID of your decrement button to **decBtn**. 4. Change the ID of your text element to **countTxt**.
Show screenshot ![properties and events](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/1e18b1f1-643c-40fb-839e-e0ee7f3dd806/142720ae-f3e7-4eec-8a87-16b469ce9014.png)
* * * ## Step 4 | Add Code to Your Elements Now it's time to make the widget do what it's supposed to do: count up and down when the buttons are clicked.  1\.  Go to the bottom part of your screen to add code to your widget. 
Show screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/2fd445d5-e777-4a22-919a-86e27f255d03/c714cacf-32e8-42c5-8e14-52bf9187397a.png)
2\.  Add the following code **before** your **onReady()** function. ```javascript //Set the count to 0 let count = 0; //Assign the count to your text element function render() { $w('#countTxt').text = count.toString(); } //Add a certain amount to the count and fire an event in the widget API function addToCount(amount) { count += amount; render(); } ``` 3\.  Add the following code **in** your **onReady()** function.  ```javascript $w('#incBtn').onClick(() => { addToCount(1); }); $w('#decBtn').onClick(() => { addToCount(-1); }); render(); ``` 4\.  Click the **Preview** button to see your widget in action.  * * * ## Step 5 | Make Your Widget Customizable with Widget API Blocks lets you [define an API for your widget](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-api/about-the-widget-api.md), so that the user (a site owner who installs the widget) can customize it to their needs. The API can contain properties, events and exported functions. Let's say that you want a user to be able to decide what the "step" of the count is. Perhaps they want the step to be 2, so that the count is 0,2,4,6 etc.  To do that, let's define a property in the widget API named `step`. ### Define the `step` Property 1. Click the **Widget API** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2021/10/27/4e37c3ae-fd88-43a8-a374-0c6c958d897f/3db01ac0-dd5b-4e14-b878-16a6a0674532.jpg)  icon to open the Widget API panel. 2. Click **Add New Property** in the **Properties** section (or hover over the section and click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/11/01/c319bb4a-e946-4229-9abd-6f4ad77f67e6/d176d48f-2f1c-45f0-b4bd-ea403358ae49.png)  icon that appears).
Show screenshot ![define new property](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/10/27/af66ea44-ad74-438b-9b44-e09e76680979/972a285f-1fcb-4fc5-aee2-38043f2073d5.jpg)
### Configure the property 1. Name your property "step". 1. Give it the type **Number**. 1. Give it a default value of **1**. 
Show screenshot ![the step property](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/10/27/4cfb0aaf-ab77-4b08-96a1-39f47f27ed72/af5fa679-3a56-4dbb-809b-7c3b5d0544be.jpg)
### Change Your Code to Consider Your New Property Change your code so that when the buttons are clicked, **addToCount** is called with the step from the properties rather than with the default value of 1. Use _$widget.props_ to access your properties (notice the autocomplete Velo suggestions).  ```javascript $w.onReady(function () { $w('#incBtn').onClick(() => { addToCount($widget.props.step); }); $w('#decBtn').onClick(() => { addToCount(-$widget.props.step); }); render(); }); ``` ### Test Your Property in Preview Mode 1. Click **Preview** to move to preview mode.  2. Click **Test API Properties**. 
Show screenshot ![test api properties](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/e13ac194-8b23-45ab-89b2-9a4b13242efd/e9f325ab-9466-4bd8-8174-e06ed2d3dcf0.png)
3\.  Change the step to a different number and see how your widget works. 
Show screenshot ![change step](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/10/27/aa0da482-efba-4f57-b1b0-edef9a8af4c8/921f5840-e5a5-48d2-b658-e6e55932a329.jpg)
### Add a Public Event to Your Widget API The widget API allows you to add an event that is fired whenever you decide.  Add an event that is fired when the "count" variable changes: 1. Click the **Widget API** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2021/10/27/4e37c3ae-fd88-43a8-a374-0c6c958d897f/3db01ac0-dd5b-4e14-b878-16a6a0674532.jpg)  icon to open the Widget API panel.  2. Click **Add New Public Event** in the **Events** section (or hover over the section and click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/11/01/4812e078-ae7e-4b2e-980b-a090cb734f45/79c543fb-71dd-4dbc-aec7-74f0b7c00e00.png)  icon that appears).  3. Name your event and describe it
Show screenshot ![new event](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/e1fa6fcf-08e4-4d92-a691-397f0fa40489/d98ef818-a6cf-42ff-9892-9fcdb21a399b.png)
4\.  Add this line to your **addToCount()** function, to fire the event when the count changes.  ```javascript $widget.fireEvent('change', count); ``` 5\.   Now, your **addToCount()** function should look like this:  ```javascript function addToCount(amount) { count += amount; render(); $widget.fireEvent('change', count); } ```
Tip: You'll need to catch your event in the site editor once you have installed your widget in your website (this will be describe in Step 8).
### Add a Public Function to Your Widget API Create and export a **reset()** function, so that site owners can easily set the count to 0.  1. Click the **Widget API** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/12345678-1234-1234-1234-1234567890ab/2021/10/27/4e37c3ae-fd88-43a8-a374-0c6c958d897f/3db01ac0-dd5b-4e14-b878-16a6a0674532.jpg)  icon to open the Widget API panel.  2. Click **Add New Public Function** in the **Functions** section (or hover over the section and click the ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/11/01/4812e078-ae7e-4b2e-980b-a090cb734f45/79c543fb-71dd-4dbc-aec7-74f0b7c00e00.png)  icon that appears).  3. A new empty function by the default name **myPublicFunction()** is created, with a designated JSDoc (the comments block) that can be read by anyone who will use your function on a site. 
Show screenshot ![empty function](https://wixmp-833713b177cebf373f611808.wixmp.com/images/eb294ad61ad7c50f8422da9f948f8f15.png)
4\.  Paste the following code instead of the function default code:  ```javascript export function reset(){ //This function is part of my public API count = 0; render(); } ``` 5\.  Update the JSDoc. You must keep the function's annotation in this format.   ```javascript /** *@function *@description Sets count to 0 and renders. *@returns nothing */ ``` * * * ## Step 6 | (Optional) Configure Your Widget's Edit-Time Behavior The **Configuration** tab within the **Editor Experience Panel** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/63dbfda0-a43d-4970-87d6-189c56ed4e51/855a0542-e46e-4cd1-9ffb-ac23df8fc7d6.png)  allows you to determine the behavior of the widget and its elements when a site owner edits it in the editor.  Try out a few options in the **Configuration** tab.
Show screenshot ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/12b3f6b30d295d70a2c86d49ebef45b0.png)
Set a display name for your widget elements:  1. Select the decrement button. 2. Change its display name under **Component Name** to "My Decrement" in the Inspector ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/f17c44b5-38a3-4277-a82f-dedae6e37557/db339269-a960-4738-b5bf-2c294e6f4761.png)  panel.
Show screenshot ![decrement button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c7e86ee41cda6bf129b280464ab771c0.png)
Prevent the text element from being selectable in the editor:  1. Select the text element that represents your count.  2. Click the **Can be selected** option in the **Behavior** section of the **Inspector** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/85c931fb-2a09-49f7-a042-ea1d8f162c7c/d074e6a3-af5d-4d3d-bbfb-a3e255568b94.png)  panel to remove the blue checkmark. 
Show screenshot ![can be selected](https://wixmp-833713b177cebf373f611808.wixmp.com/images/221258a4e64ec75a807f94d13ea324dd.png)
* * * ## Step 7 | (Optional) Create More Design Presets Blocks allows you to create various variations for your widget's design and layout, through [design presets](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-design/create-and-manage-design-presets.md). To create another design preset:  1. Click the **App Interface** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/05/01/e381745f-b13b-46fe-a339-23e286e3ee76/5c93dafc-bc3c-4197-a7f2-1cb92acf67a4.png)  panel .  2. Go to the **Design Presets** section. 3. Click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/11/01/c319bb4a-e946-4229-9abd-6f4ad77f67e6/d176d48f-2f1c-45f0-b4bd-ea403358ae49.png). 4. Click the **ֿMore Actions** icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/05/24/e9f11af0-679c-4bf1-8b76-e50e26ec98f6/27128ae4-51ef-4cdd-b679-854d2e85973b.jpg)   to rename your preset (you can also duplicate or delete it). 
Show screenshot ![add design preset](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/a0fbdcd2-da90-4cee-89eb-288843484c06/62fbd714-b93b-422c-a3b0-1bf564aa08d0.png)
4\. Make some visible changes in your widget, like changing the color of the middle section.
Show screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/04e9e285-561b-41cd-8280-a0784e247a9e/a746fb8b-3500-4e24-9d63-5f5ec49b6196.png)
Move back and forth between presets to see the differences.
Tip: Note that some design and layout changes impact only your current preset, while others impact all ([learn more](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-design/create-and-manage-design-presets.md#make-changes-per-preset-versus-global-changes)).
* * * ## Step 8 | Catch Your Event in the site editor Go to your site editor to catch the event from your app. First you need to register an Event Handler for the change event: 1. [Install your app](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md) on a site and add the widget to the site. 2. Enable **Dev Mode** on the site.  3. Click on your widget and then click the **Properties and Events** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/10/25/b8b2d885-6d3d-45ce-b4d1-ad335143e391/c4cb59f0-340d-46ed-989c-2f1f56b3cae3.jpg)  icon. 4. Change the ID of your widget, for example, to "counter1". 5. Click **onChange( )** under Event Handlers. 6. Select **counter1\_change** in the box.
Show screenshot ![counter change event](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/05/10/faf94d96-b2fc-4250-a583-fc4ad766af9b/744aeb30-91e5-43cb-aea3-5fb9bd23cdab.png)
Now write the function. It gets the \`count\` as a parameter and resets the count when it gets to whatever number you decide (in our example: 30). Your function should look like this: ```javascript export function counter1_change({data: count}) { // This function was added from the Properties & Events panel. To learn more, visit http://wix.to/UcBnC-4 // Add your code for this event here: if (count > 30) { $w('#counter1').reset();} } ``` Preview or publish to see your site in action. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Countdown Widget with Blocks ## Article: Creating a Countdown Widget ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-countdown-widget-with-blocks.md ## Article Content: # Tutorial | Create a Countdown App Using Blocks
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
This tutorial shows you how to build a countdown widget, which counts down to a specific date. It also contains a registration widget where site visitors can register and receive an email notification the day before your event. You will also learn how to install your countdown widget on a site and explore all the key features of Blocks. ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/f5b73e4f-7108-4d53-8479-051585eff68c/09cc3e81-decc-478d-b736-4abb6bfb810e.gif) ## What you get in the template To make things easier, we've created a template so that you can get started right away. The [template](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=67720f0c-6b4b-426f-aca4-918556dd36ab&http_referrer=documentation) contains: * A countdown widget. You need to complete its design and code its functions and properties. * A registration widget which you need to complete and add to your countdown widget. * Some public utilities that you can use to implement your widget's functionality. * Some backend code that you can use to implement the functions of your email notification. Get the [template](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=67720f0c-6b4b-426f-aca4-918556dd36ab&http_referrer=documentation) ## Step 1 | Complete the Design of Your Widget To build this widget, start with this [template](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=67720f0c-6b4b-426f-aca4-918556dd36ab&http_referrer=documentation) and edit it under your Wix account. The template has been left incomplete so that you can learn how to add elements to your widget. The widget is designed with a grid so that you can easily align and place elements. 1. Open the template.  2. Click **Countdown** under **App Interface**.  3. Click **Add Elements** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/7e9d6f8b-35db-4cf1-a7fe-4ddb986eb57c/5bd7d8c7-bb8f-4a6b-b34e-8d80bf5ff1c5.jpg)  in the top bar. 4. Select **Text**. Drag and drop a **48px Title** box into your widget. 5. Click **Edit Text** and change the text to **SS**. 6. Change the text color to white and center the text using the **Inspector** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/7de6d233-3a18-4ea8-98ee-55f8ee0446c0/a15b0baf-86f5-4566-a434-726efc4cb811.png) .  7. Select the text box and move it into the grid square above the SECONDS label using ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/134e6519-2581-42bf-98b4-cca8a97083c5/ddfb2d91-34e7-4c1b-ad36-c01f94a2433a.png) . 8. Resize it using ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/41b174db-5c43-45ad-b010-7b179e22723e/c0616cbf-d732-4d3d-a616-ec14a65c2195.png) . Learn more about [designing your widget](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/get-started/a-blocks-app-workflow.md) * * *
See how it looks (editing text) ![edit text elements](https://wixmp-833713b177cebf373f611808.wixmp.com/images/083170f5402f6ab563818248f6ed3442.png)
See how it looks (adjusting the layout) ![seconds text box](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/27484c1a-1034-4d66-832c-4c854d09fb37/5a431e1c-7ea7-418c-adbd-fcb786dbac75.png)
* * * ## Step 2 | Add and Define Your Widget's API Properties You can [define an API for your widget](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-countdown-widget-with-blocks.md), so that the site builder who installs the widget can interact with it through code. The API can contain properties, events and exported functions. This widget uses two properties: * **endDate**: Defines the end date when the countdown reaches zero. Using this property, site builders can change the end date so that the widget is customized for their site. * **emailId**: Determines the email message that will be sent to users when they sign up for a notification. Learn more about [widget API properties](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-api/blocks-widget-properties.md) ### Give your widget elements an ID First you need to give your widget's elements an ID so that you can easily refer to them in the code. 1. Click on the text box. 2. Give the text box an ID in the **Properties & Events** ![properties icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/25/c7256f96-e80b-4fa6-8998-db91d11f98e3/e404b243-c33a-4d17-8c87-b0f2dd651945.jpg) panel. In this example, the ID is `secondsTxt`. Note that the other elements have already been given IDs. ### Define the "endDate" and "emailId" properties 1. Click the **Widget Public API** button. 2. Click **Add New Property** in the **Properties** section. 3. Enter the property name, `endDate`. 4. Select the property type. In this case, **Date and Time** (the date is displayed in US format mm/dd/yyyy). 5. Select a default value (the site builder will be able to change this when they install your widget in the editors). 6. Hover over **Properties** and click the ![add icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/06/10/be929c05-3c41-452d-ae3a-1503546f8883/b6abb229-8c19-4d2b-9522-d42d125dcc76.png) icon that appears. 7. Create another property for the triggered email and call it `emailId`. This is a **Text** type property with no default value. * * * ## Step 3 | Add Code to Make Your Widget Work To make your widget count down, you need to create a function in the code that calculates the remaining time until the end date. To do this, your template comes with some pre-installed utilities that you can find in the **Public & Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/04/d47aba86-0d01-4d2a-bdf2-b04985946a9a/9ced50f0-4a85-4e63-98fc-4f3e8762ee8c.jpg)  menu.  Blocks uses a new global variable, **$widget** and the property, **$widget.props**, that holds all the properties that you have defined for your widget. Blocks also uses Velo autocomplete, so that you can write code more easily. Learn more about the [widget API](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-countdown-widget-with-blocks.md) ### Change the code to consider your new function 1. Click on the **Code** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/04/d47aba86-0d01-4d2a-bdf2-b04985946a9a/9ced50f0-4a85-4e63-98fc-4f3e8762ee8c.jpg) menu. 2. In the code panel for **Countdown** create a function, `updateTime()`, so that your countdown widget will update the count. Your new function should look like this: ```javascript $widget.onPropsChanged(function () { }); function updateTime() { const {days, hours, minutes, seconds} = getRemainingTime (new Date($widget.props.endDate)); $w('#daysTxt').text = days.toString(); $w('#hoursTxt').text = hours.toString(); $w('#minutesTxt').text = minutes.toString(); $w('#secondsTxt').text = seconds.toString(); } ``` You also need to set an interval for how often the widget updates the remaining time. To do this, add the following code **in** your `onReady()` function. This updates the widget every second. ```javascript $w.onReady(function () { updateTime(); if (wixWindow.viewMode !== "Editor") { setInterval(updateTime, 1000); } }); ```
**Edit time condition** Velo code only runs when you click preview or when you open the live site. The Blocks **onReady** code runs also during editing time, so that you can see it in action while working in the editor. We use the above condition so that your countdown widget doesn't count down during editing time.
### Preview your widget and test its API properties You are ready to preview your widget and its API properties. 1. Click **Preview**. Your widget should countdown every second to the end date. 2. Click **Test API Properties**. A panel appears with the default value you set earlier.  3. Change the date to check if the widget responds and counts down to the new date.
See how it looks ![test api properties](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/12/0b5c4b84-eba0-4976-9c25-1f34e89e27ce/fee00975-4b7c-4364-85aa-2da9d3c05907.gif)
* * * ## Step 4 | Create and Code a Custom Panel You can create a custom settings panel so that site builders can change the settings of your widget when they install it on a site. In this example, the custom settings panel allows the site builder to change the end date and define an email ID that determines which email will be sent to users when they register to receive notifications. Learn more about [panels](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-panels/about-panels-in-blocks.md) ### Create a custom panel 1. Click on the **Custom Panels** tab in the **Editor Experience** menu. 2. Click **Create Panel**. 3. Name your panel. In this example, use "My settings". 4. Click **Create Panel**. 5. Click **\+ Add Element**. 6. Select **Text Input**. This will be for the end date. 7. Click **Properties & Events** ![propeties icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/25/c7256f96-e80b-4fa6-8998-db91d11f98e3/e404b243-c33a-4d17-8c87-b0f2dd651945.jpg) . Give your text input an ID - `endDateInput`. 8. Select the text element and click **Settings**. 9. Add the name "End Date" in the **Field Title** field. 10. Delete the text in the **Default Text** field. 11. Set the placeholder text to "End Date". 12. Add a text divider. 13. Click **Settings**. Add a title in the **Section Title** field. In this example, use "Triggered email". 14. Add another **Text Input**. Give your text input an ID - `emailIdInput`. 15. Select the text element and click **Settings**. 16. Add the name "Email ID" in the **Field Title** field. 17. Delete the text in the **Default Text** field. 18. Set the placeholder text to "Email ID". The design of your custom panel is complete. Now you need to implement it by adding code.
See how it looks ![settings custom panel](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/30/faa8fae4-9464-4528-892c-b364138b91f2/6559df8f-007e-4ae8-87cb-245dc91a1263.png)
### Add code to your custom panel You want your custom panel to display the current end date and email ID when it loads. The site builder uses the panel to change these values. You need to add code to the panel so that when these inputs change, it updates your widget. Learn more about [adding code to your custom panels](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-panels/add-code-to-custom-panels-in-blocks.md). To enable panel elements to interact with your widget and perform actions in the editor, you can use the Velo `wix-widget` module in your code. To use the Widget API, import `wixWidget` from the `wix-widget` module. Insert the following code **before** your `onReady()` function. ```javascript import wixWidget from 'wix-widget'; ``` You want your widget to update when the values change, so you need to register an `onChange` event. You also need to add `async` to your `onReady()` function as you will be using some asynchronous functions. Your `onReady()` function should look like this: ```javascript $w.onReady(async function () { const { endDate, emailId } = await wixWidget.getProps(); $w('#endDateInput').value = endDate; $w('#emailIdInput').value = emailId; $w('#endDateInput').onChange(e => { wixWidget.setProps({ endDate: e.target.value }); }); $w('#emailIdInput').onChange(e => { wixWidget.setProps({ emailId: e.target.value }); }); }); ``` Click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/31/c441d6e2-852d-4b01-819e-e5f16a84f5dd/9e2696fd-f012-4e2d-bbbd-7d8f0de70c45.png)  **Run** or **Preview** to check your code
See how it looks ![settings panel code preview](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/30/5c89b383-ae8e-4401-a5a7-7d8700913388/4474db44-3092-4c89-bb5c-775cb69755d1.gif)
* * * ## Step 5 | Configure Your Widget Now that your panel is designed and coded, you need to configure your widget so that your panel connects to one of your widget's action bar buttons. The **Configuration** tab within the **Editor Experience Panel** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/89cc2cba-4f66-4813-85bd-4d9904cae791/055c9aa5-3dc2-44f6-b0ed-425fb3fee6f2.png)  enables you to control how your widget behaves when a site builder installs and customizes it on a site. You can give your widget and its elements display names, so that it is clear to site builders what your widget does. You can also make changes to the floating action bars that appear in the editors when site builders select elements in your widget. Learn more about [configuration](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-configuration/about-configuration-in-blocks.md). ### Add your custom panel to your widget's floating action bar 1. Click the Editor Experience Panel icon ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/89cc2cba-4f66-4813-85bd-4d9904cae791/055c9aa5-3dc2-44f6-b0ed-425fb3fee6f2.png) . 2. Select **Configuration**.  3. Select your countdown widget. A floating action bar appears. 4. Click **Settings**. 5. Click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/04/520f26e0-3449-4409-bc59-acb658f7555e/0f09fa28-68e4-4aec-84e4-effb07a8cd90.png)  **Action Button Settings**. The **Main Action Settings** panel appears. 6. Select **My settings** from the dropdown list to select your custom panel.
See how it looks ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/12/7b909ba7-417e-45dc-8450-521a9ea3a64f/0bc3fc45-d3fb-4a86-991c-676a2be8a5e0.png)
* * * ## Step 6 | Add a Second (Inner) Widget and Implement the Registration Logic The template comes with a second widget called **Registration.** You can find it under **App Interface**.  When you click **Layers** you can see that it has been created as a multi-state box with three states. It has a button labeled **Register**, which changes to **Submit** when the site visitor clicks it. It also has a field box where site visitors can add their email address. A thank-you message appears when a site visitor has registered. This widget also comes with a design preset which can be used for mobile view. Learn more about [design presets](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-design/about-design-presets.md) ### Add your registration widget to the countdown widget You're now ready to add your registration widget to your first widget, the countdown widget. In Blocks, you can create lots of different widgets and add them to other widgets.  Learn more about working with [widgets within widgets](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-widgets/add-nested-widgets-in-blocks.md) 1. Working in the **Design** tab, select your countdown widget. 2. Click **More Options** ![more options](https://d2eyqiy4n03ve6.cloudfront.net/12345678-1234-1234-1234-1234567890ab/2015/08/03/ea5aa155-aa5e-4ef1-8bea-69375765e8c4.png) and select **Add Widget**. 3. Select **Registration**. Your inner widget appears in the middle of your countdown widget. 4. Drag and stretch your inner widget to fit the lower section of your countdown widget. 5. Change its ID to `registration` in **Properties & Events** ![propeties icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/25/c7256f96-e80b-4fa6-8998-db91d11f98e3/e404b243-c33a-4d17-8c87-b0f2dd651945.jpg).
**Another way to add a widget** You can also add an inner widget by clicking the  ![add button](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/08/65319986-7b71-49dc-9c0a-973c4ae3536c/e7978e5c-5c79-487b-8b3e-d78b9947ac57.jpg) **Add** **Elements** menu. Select **MY WIDGETS** and drag and drop the widget you want to add into the first widget.
See how it looks ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/e61248c6-db97-44c7-8bb8-914d5e4fc62f/48eac3d1-b58e-4b4d-bac5-57f9208549a5.png)
See how it looks ![drag and stretch inner widget](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/12/4bfd8693-c902-465a-ac43-ef2b3435839e/5540e47b-9396-45e7-8211-640aa2ef05d5.gif)
### Implement the registration logic in the main widget's code You'll implement the code for user registration in the main widget (Countdown), using functions that are defined in the `contactUtils.js` and `collectionUtils.js` files. The `onSubmit()` function creates or updates a contact using the `wix-crm` API via the `createContact` utility, then stores the subscription data in your app's collection. The function receives the user's email address from the registration form and updates the collection. 1. Create a new async function. 2. Call it `onSubmit`. It uses the create contact utility from `contactUtils.js` and the collection name helper from `collectionUtils.js`. Your code should look like this: ```javascript async function onSubmit({ email }) { const contact = await createContact(email); wixData.insert(getSubscriptionsCollectionName(), { endDate: new Date($widget.props.endDate), emailId: $widget.props.emailId, contactId: contact.contactId }); } ``` ### Register for the onSubmit event In your `onReady()`, after your `updateTime()` function, add the following code: ```javascript $w('#registration').onSubmit(onSubmit); ```
**Backend code is already included** The backend code is already implemented in your template in Blocks. It includes an export function, **notify()**, which uses the pre-installed utilities. It checks the remaining time, triggers the email when ready and updates the collection. You need to invoke this in the editor, as explained below in Step 10.
* * * ## Step 7 | Release Your App and Give it a Namespace Now you are ready to release your app for the first time. Your first release will be a major version. Later, when you are working on your app, you can choose a minor or major version. A minor version is updated automatically on all the sites where it appears. You just need to refresh the page. In a major version, the site builder needs to update your app manually. An indication appears next to the name of the app in the **Installed Apps** panel in the Editor.  Learn more about [versions in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/manage-blocks-app-versions.md) When you click build for the first time, Blocks prompts you to give your app a namespace. This namespace is used to refer to your collection in Velo code in the editors and in the app’s code in Blocks. Learn more about the [app namespace](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-the-recipes-app-with-blocks.md) ### Your first release 1. Click **Release**. 2. Enter a namespace for your app and click **Next**. 3. Select **Major Version** and click **Release**. 4. You get a message that Version 1.0 is built. Click **Got it** to continue working on your app. ## Step 8 | Add a Collection to Your Widget Now you need to create a collection to store all of the subscriptions. Collections in Blocks are empty placeholders that you design in Blocks, which will be filled with data once the app is installed on a site. Learn more about [collections in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/connect-elements-to-collection-fields-with-no-code.md) The template has a pre-installed utility called `getSubscriptionsCollectionName()`, in **collectionUtils.js** under **Public & Backend**. It constructs the full name of the collection so that you don't have to add your full app namespace every time you refer to it in the code. ### Configure your collection utility To configure your collection utility: 1. Click **collectionUtils.js** under **Public & Backend**. 2. Add your app's namespace. Your code should look like this, with your own namespace: ```javascript const NAMESPACE = '@mywixaccount/my-app-namespace'; export function getSubscriptionsCollectionName() { return `${NAMESPACE}/subscriptions`; } ``` ### Create your collection To create your collection: 1. Click **CMS**. 2. Click **+ Create collection**. 3. Give your collection a meaningful name. In this example, use **Subscriptions**. 4. Click **Create**.  ### Add fields to your collection 1. Click **Add Item** to add a field to your collection. 2. Enter **Title**, for example **Subscription**. 3. Click **Add Field**. 4. Select **Date**, click **Choose Field Type**. 5. Enter **endDate** in the **Field Name** field. This will be the last date up until which people can register. 6. Click **Save**. 7. Now click **Add Field** and create a **Text** type field and call it **emailId** for the triggered email. 8. Add another **Text** type field and call it **contactId**. This will store the contact details of registered users. 9. Now create a **Boolean** type field and call it **notified**. This ensures that subscribers will be notified only once.
See how it looks ![add field in subscriptions](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/04/24dfdee6-73d5-467e-bcdb-c33b89672e4b/52a0a7d7-3c94-42c5-99fa-bd56f8288d39.png)
### Set permissions for your collection You want any site visitor to be able to add content to your collection by subscribing.  1. Click **More Options** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/05/ae46274e-c846-4af2-ae94-3392a416ee15/d105bc7d-6e3e-4a3f-b9ea-18aad96248e6.png) for your collection from the **CMS** menu. 2. Select **Permissions & Privacy**.  3. Select the dropdown menu **Who can view this content?**. 4. Select **Anyone**. 5. Click **Save**.
See how it looks ![collections permissions and privacy](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/04/03567fc0-c0fe-4322-96c6-23ff51462873/4246826e-d88b-4b15-a087-4195da644b90.png) ![edit collection settings](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/05/90847e84-9da5-495f-99a8-6da370a6fd24/9da423fd-3f16-427e-b94d-1792a0ee2323.png)
* * * ## Step 9 | Install Your App on a Site in the Wix Editor You can install your widget on any of the editors. The following example uses the Wix Editor. Before you install your app, you need to release your app a second time. This will be a major release, because you have created a collection. 1. After releasing your app, open your website. 2. Click **Add apps** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/13/f834347c-197b-4567-b41c-0223896006be/c7ee749e-357c-4a5b-95da-7e551b526d73.png) .  3. Click **Custom Apps**. A list of all your apps appears. 4. Select your app from the **Available apps** list. 5. Click **Install App**. 6. Click **Add** **Elements** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/13/d5302806-c42f-4e0e-9f2b-8d63be2a4524/0a96ba99-7bb7-4f35-afb4-efa49776f90b.png) . 7. Select **My Widgets**. 8. Double click your widget to add it to your site.  Your widget is fluid and you can select its elements so that you can customize the widget to your site. You can also open the settings panel and change the default end date. 
See how it looks ![countdown widget installed in the editor](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/08/31/e2d8fcc5-7225-4da9-9fd5-b82c373fe11e/dc03bc9d-38d4-4c1b-b3e0-790de4474d4f.gif)
### Set an Email ID Users can register for an email notification. You need to create the email that will be sent and get an ID for it so that the widget API can identify which email needs to be sent. 1. Open **My Dashboard** from the **Site** menu in the top bar. 2. Go to **Triggered Emails** in **Developer Tools**. 3. Click **Get Started**.  4. Enter a subject. 5. Design the email using the editor tools. 6. Click **Save & Continue**. 7. Add the sender details, the **From name** and the **Reply-to email**, and click **Save**. 8. Click **Got it**. 9. Click **Save & Publish** again. You get a generated identification code that links to the email that you have designed so that it will be sent to anyone who registers.  10. Enter this code in your custom panel in the **Email ID** field. ### Use Backend Code in Wix Editor to Notify Subscribers Backend code is included with your app in Blocks, but you need to invoke the notification in the editor. ### Invoke your notify function 1. Click **\+ New web module** under **Backend** in **Public & Backend** in the Wix editor. 2. Call it `backend.web.js`. 3. Import the backend function in your site's code section under **backend.wb.js**. Your code should look like this: ```javascript import { notify } from 'myWixId/my-application-name-backend'; ``` Now create an export function `invokeNotify` in **backend.jsw** in your site's code section. Your code should look like this: ```javascript export function invokeNotify() { return notify (); } ``` Now you need to create a [scheduled job](https://dev.wix.com/docs/velo/articles/getting-started/schedule-jobs.md). The widget's frontend code only runs when someone is on the page. You need a backend process that runs even when no one is visiting to check who should be notified and send emails. A Scheduled Job is the built‑in way to do that. 1. Click **Add** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/7ef91d19-d5d6-41b5-9f31-4a03269c9a61/b3f3cded-858f-4bb2-918e-d9c00f912567.png) in **Backend** 2. Click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/6b5c7381-912e-4754-99e2-d23157db088c/2022/09/01/e0f3d35c-2ffa-4cc2-9a47-a8e01ea1c99c/cb360e28-8c01-4af5-b657-8596f865778a.png) **Add scheduled jobs**. Add the following code under **jobs.config** in your site's code section. In this example, the notify function is invoked at 10 minutes past the hour, every hour. Your code should look like this: ```javascript { "jobs": [{ "functionLocation": "/backend.jsw", "functionName": "invokeNotify", "description": "", "executionConfig": { "cronExpression": "10 * * * *" } }] } ``` > **Note:** Scheduled jobs run only on the published site. Expect up to 1–2 minutes delay. * * * ## Step 10 | Test Your App Now you are ready to test your app.  1. Publish your site.  2. Register to receive an email notification.  3. Go back to the editor and check your collection.  4. You can see that there is a new subscriber. Wait until the countdown reaches 0 days and you'll get a notification email. You will also see a tick in the **notified** field.
**Check your triggered emails** You can also check in **Triggered Emails** under **Developer Tools** in your dashboard to see if the email has been sent.
--- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create the Recipes App with Blocks ## Article: The Recipes App ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-the-recipes-app-with-blocks.md ## Article Content: # Tutorial | Create a Recipes App Using Blocks
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
This tutorial walks you through building the **Recipes** app. The app showcases recipes on Wix sites, so site visitors can see an image, description, and rating for each recipe. They can also subscribe to get new recipes.   The app includes: * 2 widgets - **Recipes** and **Subscribe** * A custom settings panel * A collection for holding the recipes data * A dashboard page for allowing builders to interact with data in the app **Ready to begin?** [Create your own copy of the app](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=0f424cdc-4998-4c66-8a6e-19be9a9a3c23&http_referrer=documentation) in Blocks. The link leads to a template with most of the design and some of the code ready.  >**Notes:** > Throughout this tutorial, you'll move back and forth from creating the app in Wix Blocks to testing it on a Wix site.
![recipe app](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/17/05e14192-58c5-4c5d-a7ae-0613ca96992e/808ff254-5e3c-4c0c-96ce-f02f6ba1f573.png)
* * * ## Step 1 | Create a new Design Preset [Design Presets](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-design/about-design-presets.md) are Blocks' way of allowing you to have multiple designs and layouts for the same widget. They're often used for adapting the app for different devices, like desktop and mobile. The first thing we'll do is create another preset for when the widget is used on a mobile phone. This preset will be a duplicate Card Layout preset, with a few changes.  1. In case you missed this first step: click [this link](https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=0f424cdc-4998-4c66-8a6e-19be9a9a3c23https://blocks.wix.com/wix-blocks-new-app-creator?editorType=RESPONSIVE&originTemplateId=0f424cdc-4998-4c66-8a6e-19be9a9a3c23&http_referrer=documentation) to create your own copy of the app. 2. Name your app: go to the top bar and click on the app's temporary name. 3. Go to the **Design Presets** menu. The app template already has 3 presets and you'll create a new one now.  4. Duplicate the **Card Layout** preset (to do this, hover, then click the ![Duplicate preset icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/6937c4d8-69a7-43f7-87b9-cd2e7d2992e7/c70f0391-204b-418b-b8d0-7a661495ce95.jpg) icon).  5. Rename the new preset to **Mobile Layout** and make sure it's selected.  6. On the right side of the screen, go to the **Design** menu of your widget. 7. Click on the **Layers** ![layers](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/f7b41afc-940b-4dad-8570-be5a7c71ae97/38653f43-8499-4015-8f3f-37e02e5c3ec2.png) menu and select the entire widget. 8. Change the widget's width to 320px. 9. In the **Layout** section, click **Rows**. 10. Select your widget's first grid row. Change the first grid row height to 150px.
See screenshot ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/47cbebdc59adde0e0cb347aabac1df23.png)
11.  Go to **Layers** ![layers](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/f7b41afc-940b-4dad-8570-be5a7c71ae97/38653f43-8499-4015-8f3f-37e02e5c3ec2.png) and click on the first box in the **#recipesRepeater**. Click on the **#recipesTitle**.  12.  Change the title's font size to 18.
See screenshot ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f524fbbc4a4ff6ff47a18babd0245168.png)
13. Click on the **Description**, click ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/12/06/eaa05edd-0484-4992-bd73-cc035912ae68/48463e03-f5cf-47a5-81bc-42d0044bf84f.jpg), then **Display**, and set it to **Hide on this preset**, to hide the description in mobile.
See screenshot ![hide description](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8593cabf9ea9a88ce1223da17f0acda7.png)
12.  Go back to the **Card Layout** preset and check that the description **does** appear there. * * * ## Step 2 | Test the app Test your app on a site to see how it works. 1. Click **Test** on the top right. 2. You will be prompted to create a [namespace](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/code-in-blocks/creating-a-namespace-for-your-app.md) first. Your namespace allows your app to interact with data collections and code files in its code.   3. Create a [test version](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/test-your-app-on-a-site.md). 4. Select a site to test your app on. If you don't have a test site, click **Create Dev Site**. 5. Add your widget. Go to the **App Widgets** section of the **Add** ![Add panel icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/8bd603e1-b2ab-4084-890c-9be46bea7546/1e172246-819f-4f89-8083-c72a6b96bf52.png) panel. Click or drag the Recipes widget to add it to your site.
See screenshot ![add widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8e3fc0e5795a84089925d007b920f846.png)
Now let's do some actions on this widget in the site, just to see how it works. 1. Click the **Stretch** ![Stretch widget icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/20b8e657-f5de-41a1-802a-3425e8110a69/50ca7b0b-3348-4d1a-84ef-b78677e81686.png) icon to stretch your widget in the site section.  2. Go to your desktop ![Desktop view icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/92b0cef9-e9e6-45c8-a68b-6cff675a2f47/ab1ed920-236a-4756-ab5b-380245d4c529.png) view. Click the **Presets** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/05/27/2907ce70-8afe-48d0-a3b7-65097674f11c/19742c50-14ba-48de-a648-f09689a259cf.png) action button of your widget and change the preset.  3. Go to your mobile ![Mobile view icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/05/a310c43d-7d52-4540-ae48-8d7eda8a2ea1/1d3e1f7c-993a-4b5f-8f62-bbd89bc3d934.png) view and change your preset again to the **Mobile Layout** preset. 4. Right-click on the widget and click **Reset Widget**, to go back to the original design.  * * * ## Step 3 | Add the recipes collection in Blocks Now, go back to Blocks to continue creating the app. 1. Go back to Blocks. 2. Click the **CMS** icon. 3. Create a collection called **recipes**. 4. Add the following fields (make sure that the names are **exactly** the same, no caps): * **image** type **Image** * **description** type **Text** * **duration** type **Text** * **rating** type **Number** 5.  Go to [our demo site](https://wixblocks.wixstudio.io/cookingwithmaria), search for the words "Collection data" and save the data file to your computer.  6. Import the data to your collection.
See screenshot ![import](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/02/05/ed7d6e8e-6562-4ea5-9242-9382fecc74f3/480ebf3b-84f7-4910-9428-4e3de9090d2a.png)
7. Click **More Actions** and select **Collection settings** from the dropdown. 8. Copy the collection ID.
See screenshot ![copy](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2b2a4307a151ccdecd8573dd59878bf5.png)
8. Go to **collectionUtils.js** file in the **Public and Backend** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/5017b79e-7eee-41c5-985d-28067e549376/efbd7898-3f64-4ed1-b25b-77505a08b8cd.png)  files. This is the file for interacting with the app's collection.  9. Change the value of **collectionName** from '@wixBlocks/recipes-app/recipes' to your collection ID.
![change collection name](https://wixmp-833713b177cebf373f611808.wixmp.com/images/aa8803c8fe8ffc10740854516a437c2c.png)
* * * ## Step 4 | Add logic to the Recipes widget This step populates the repeater with the data from the collection, using code. 
**Code snippets** You can also find the following code snippets in the **codeSnippets.js** file in the **Public and Backend** ![code files icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/35686f9a-c8fa-4f17-88cd-c650540cc824/8a656d1d-b4a1-4dd5-adeb-d2f3f12a041a.png) section.
1. Click **App Interface** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/04/24/93ca045a-ae3e-4d1b-941c-c79a7144fd0e/5a9379fc-820d-4438-ae56-bbe17af6a938.png)  to go back to the **Recipes** widget. 2. Paste the following snippet in your widget code.  ```javascript // import { populateRepeater } from "public/repeaterUtils"; import { getRecipesCollectionName, queryCollectionItems } from 'public/collectionUtils' ``` 3. Create an **initRepeater()** function. For now, create the repeater to show 4 items. We'll change this later.  ```javascript async function initRepeater(){ const collectionItems = await queryCollectionItems(getRecipesCollectionName()); populateRepeater($w('#recipesRepeater'), collectionItems, 4); } ``` 4. Await the **initRepeater()** function in your widget's **onReady()** function. ```javascript $w.onReady(async function () { await initRepeater(); }); ``` This is the full widget code at the moment:  ```javascript // Full Recipes widget's code import { populateRepeater } from 'public/repeaterUtils' import { getRecipesCollectionName, queryCollectionItems } from 'public/collectionUtils' $w.onReady(async function () { await initRepeater(); }); $widget.onPropsChanged(async (oldProps, newProps) => { }); async function initRepeater(){ const collectionItems = await queryCollectionItems(getRecipesCollectionName()); populateRepeater($w('#recipesRepeater'), collectionItems, 4); } ``` ## Step 5 | Create the Recipes widget API In the previous step we defined the repeater to show 4 items from the database. But what if a site builder who installed your app wants to show a different number of items? Let's add a property to the Recipes widget API. The property will hold the number of items to display in the widget, and will replace the constant number of 4 that we added in the previous step.  1. Click the **Widget API** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/3fe0b030-f3ef-4433-b565-4f6f448b910b/2db07e5e-14d7-466a-8f00-85a527b9ea2a.jpg)  icon, on the right of your widget's code area.  2. Click **Add New Property** 3. Name your property **itemsToDisplay** and give it a display name (such as: "Items to Display", which is more readable). 4. Give it a type of number and a default value of 3. 
See screenshot ![items to display](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/be852b9d-d70b-49bf-a6cc-1cf4a7ebf802/3e507c12-09cb-4fa3-ae54-a95e0b08f8d7.png)
5.  Update the **populateRepeater** function so that it sends the prop as a parameter instead of the 4. ```javascript populateRepeater($w('#recipesRepeater'), collectionItems, $widget.props.itemsToDisplay); ``` The **onPropsChanged()** function is triggered when an event of props changing is detected. You can add custom logic to this function. 6.  Await the **initRepeater()** function in your **onPropsChanged()** function:   ```javascript await initRepeater(); ``` Here is the full widget code, after adding the property.  ```javascript // Full Recipes widget's code import { populateRepeater } from 'public/repeaterUtils' import { getRecipesCollectionName, queryCollectionItems } from 'public/collectionUtils' $w.onReady(async function () { await initRepeater(); }); $widget.onPropsChanged(async (oldProps, newProps) => { await initRepeater(); }); async function initRepeater(){ const collectionItems = await queryCollectionItems(getRecipesCollectionName()); populateRepeater($w('#recipesRepeater'), collectionItems, $widget.props.itemsToDisplay); } ``` * * * ## Step 6 | The "My Settings" panel When a site builder installs your widget, they can change the value of the properties with an autogenerated **Settings** panel in your widget's floating menu. However, you can also build a custom panel and add custom logic to it.  1. Click the **Editor Experience** icon. 1. Click the **Custom Panels** tab. The app has a custom panel nameD **My Settings**. Look at the code tab. The code gets the property and sets a slider's value accordingly.  2. Add an **onChange()** function inside the **onReady()**, so that when the property is changed by the slider, the function notifies the panel to set the widget props again.  ```javascript $w('#panelSlider1').onChange(async event => { await wixWidget.setProps({itemsToDisplay: event.target.value}); }); ``` This is the full panel code:  ```javascript import wixWidget from 'wix-widget'; // Control your widget's properties, design presets, and more. import wixEditor from 'wix-editor'; // interact with the Editor by removing or restoring widget elements, opening Dashboard panels, and more. $w.onReady(async function () { const props = await wixWidget.getProps(); $w('#panelSlider1').value = props.itemsToDisplay; $w('#panelSlider1').onChange(async event => { await wixWidget.setProps({ itemsToDisplay: event.target.value }); }); }); ```
**Tip:** Note the [wix-widget](https://www.wix.com/velo/reference/wix-widget) and [wix-editor](https://www.wix.com/velo/reference/wix-editor) modules at the beginning of the panel code. They are unique to [Blocks custom panel code](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-panels/add-code-to-custom-panels-in-blocks.md).
* * * ## Step 7 | Make configuration changes The [**Configuration**](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-configuration/about-configuration-in-blocks.md) tab in the **Editor Experience** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/04/24/d885478b-0fa3-46d0-950a-801399d94383/8dc747a0-244b-4714-bd4c-7a71986a8394.png)  panel allows you to control the editor experience of a site builder who installs your app.  1. Click Editor Experience ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/04/24/d885478b-0fa3-46d0-950a-801399d94383/8dc747a0-244b-4714-bd4c-7a71986a8394.png) . 2. Go to the **Configuration** tab.  3. Click the gray part of the widget, to edit the action bar of the Recipes widget.  4. Click the **Settings** button. 5. Click **Action Button Settings** and connect it to the **My Settings** panel.  Now, whenever a site builder clicks "Settings", they will see your custom panel. 
See screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/936d66e7-fed2-4fed-8744-c45eca8e62ce/584d64e9-5744-4f2b-a674-bf0e8562a23d.png)
Another change you can make in the **Configuration** tab is to prevent layout elements from being selected. A site builder has no reason to select the repeater in your widget.  6. Select the repeater and uncheck **Element can be selected** box. 
See screenshot ![element can be selected](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/540d4880-5c57-4526-8d53-6186129185b6/a072c847-6ee3-4aee-a7e9-455610171a04.png)
* * * ## Step 8 | Check the app on your site Let's check that the app works on your site.  1. Click **Test** to test your app on the same site again. Go to your test site.   Now let's add data to the collection in the site, to check the app functionality ([learn more](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/connect-elements-to-collection-fields-with-no-code.md)).  2.  Go to the CMS ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/02/05/e590ea43-0c7f-4530-a039-84fe1dd32350/8cec73b2-dc22-4acc-982b-c0e61121bd6e.png)  and add the CMS to your site.  3. Find your collection.  4. Click on the collection name and open it. See that the default data from Blocks was imported.
See screenshot ![cms](https://wixmp-833713b177cebf373f611808.wixmp.com/images/828374f86f48b954c894c778ca10aad8.png)
5.  Click on your widget's **Settings** button and change the number of items to display.  6.  Go to preview and see that the number of items changed. If it did, your test is complete!  * * * ## Step 9 | Add the Subscription widget Blocks allows you to create another widget and insert it inside the main widget. For example, let's say that you want to allow people to subscribe to an email list through your Recipes app. To do this, we created another widget, named Subscription. However, this is just a dummy widget for the purpose of this tutorial - we didn't implement the code. This is how you add the Subscription widget to your Recipes widget:  1. Go to Blocks.  2. Open the **Add** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/390641b6-b21c-400c-a931-4e4d90a7d497/3725ffab-5485-41ad-b9d9-4da11f4ffbf9.jpg)  panel. 3. Click **My Widgets**. Here you can see all the widgets and their presets. Scroll to the end of the list.  4. Drag the Subscribe widget to be on the top part of your Recipes widget.
See screenshot ![subscribe widget](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/16/0ad23e50-f986-42b8-a8f9-05a1be12dc81/ef33736b-ee82-4ebb-ad6e-50d125cbd96b.png)
## Step 10 | Make layout changes Let's make some layout changes to adapt the Subscribe widget to the Recipes widget.  1. Click the Subscribe widget (make sure that you select the entire widget and not any internal element).  2. Go to the **Position** tab of the **Inspector** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/a163edb8-3cc6-4a76-9f8e-bd755a114854/41687890-a25a-4ae0-b119-f5a464b40822.png) . 3. Change the margins to 42px on the top and left.  4. Right-click on the Subscribe widget and select **Use on All Presets**. Now your layout changes apply to all presets.
See screenshot ![margins](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bed2548a02d65a4cc7972c3614ae6925.png)
Let's make some more changes in the mobile preset.  1.  Go back to the **Design Presets** section of **App Interface** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/04/24/93ca045a-ae3e-4d1b-941c-c79a7144fd0e/5a9379fc-820d-4438-ae56-bbe17af6a938.png)  and click your **Mobile Layout** preset. 2.  In the inspector, click on 3 dots and enable **Advanced sizing**. 3. Change the Width to 86%. 4.  Align to center - both vertically and horizontally. 
See screenshot ![in the inspector](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7d56aa60a27fd84908d0e42c32536b7c.png)
* * * ## Step 11 | Create a Blocks Dashboard Page Now we want an easy way for users to interact with the collection of recipes, so they can add and manage their recipes. For this, we have the app's Dashboard ([learn more](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/dashboard-pages/about-dashboard-pages-in-blocks.md)). When a site builder installs your app on a site, the Dashboard page will be added with it.   1. Go to your app's Dashboard page. 
See screenshot ![dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bba9bba62009ca7d3cad73a6c923661a.png)
2. Let's look at the Dashboard page. The Dashboard is made out of a large multi-state box (you can see this easily in the Layers ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/03/cc8ed645-c486-4f74-a256-641916deee1e/42c93e0f-87f3-45c1-b85d-c62ffafa4e0c.png)  panel).  The multi-state box has 2 states, one to manage all the recipes and one to manage individual recipes. You can click on each state and see the difference between them. 
See screenshot ![dashboard multistate box](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/03/ff2f8700-e2ac-4df2-87d2-c303e9d38fa6/f3de2ed6-3ab4-4ff6-9858-546ccd1bd1c6.png)
3. Look at the **#recipes** state. It has 2 buttons: * **Add Recipe:** changes the state of the multi-state box to the **form** state. * **Manage Collection:** navigates to the CMS page in the Dashboard. 4. Look at the Dashboard's code tab to see the buttons' code:  ```javascript $w('#addBtn').onClick((e) => { $w('#multiStateBox1').changeState('form'); }) $w('#manageCollectionBtn').onClick((e) => { wixDashboard.navigate('wix-databases-lazy-page-component-id'); }) ``` >**Note:** >[wixDashboard.navigate()](https://www.wix.com/velo/reference/wix-dashboard/navigate) gives you the ability to navigate >to other dashboard pages. You must enable Dev Mode in your site to do so. 5. Look at the **#form** state. It has 2 buttons:  * **Cancel** cleans the form * **Save** uploads the image and updates the repeater. 6. Look at the Dashboard's code tab to see the buttons' code:  ```javascript $w('#cancelBtn').onClick(onCancel) $w('#saveBtn').onClick(onSave); function onCancel(){ $w('#multiStateBox1').changeState('recipes'); cleanForm() } async function onSave() { let imageUrl = '' if($w('#imageInput').value.length !== 0){ const uploadedImage = await $w('#imageInput').uploadFiles(); imageUrl = uploadedImage[0].fileUrl; } await wixData.insert(getRecipesCollectionName(), { title: $w('#titleInput').value, description: $w('#descriptionInput').value, rating: Number($w('#ratingInput').value), duration: $w('#durationInput').value, image: imageUrl, }); await initRepeater(); $w('#multiStateBox1').changeState('recipes'); cleanForm(); } async function initRepeater(){ const collectionData = await queryCollectionItems(getRecipesCollectionName()); populateRepeater($w('#recipesRepeater'), collectionData); } ``` >**Note:** >The **initRepeater()** function in the dashboard code looks almost identical to the **initRepeater()** in the widget >code. They both use the functions from the **repeaterUtils.js** file. 7. In the Dashboard page, go to **Preview** and try adding another recipe. * * * ## Step 12 | Connect the dashboard to an action button 1. Go back to the **Recipes** widget.  2. Go to the **Configuration** tab.  3. Click the secondary action button, which is **Change Design**.  4. Click **Action Button Settings**.  5. Set the button to open a **Dashboard** instead of a panel.  6. Set the main action button text to **Manage Recipes** instead of **Change Design**. 
See screenshot ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/21e446eb-7f73-4f2f-84b1-c6b77e64e45e/2024/04/24/a5bf1cd8-bffb-4495-9c9e-06fff7d3cf0b/ab6a47de-3661-4792-9c0e-7799f8cd6e53.png)
Now that we finished building our app, it's time to build a major version (learn more [about versions in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/manage-blocks-app-versions.md)). 7.  Click **Release** and select **Major Version**. * * * ## Step 13 | Check the app on your site again 1. Go to your site's editor and update the app's version. 
See screenshot ![go to latest version](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/17/f641a161-d466-4ac3-9dff-545f47648906/776332f9-5bc8-479a-820a-debd418054d2.png)
2.  Open the site's dashboard. 3.  You'll see a new dashboard page with your app's name. Click on it. 
See screenshot ![recipes app studio dashboard](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/17/75487cc0-69ae-44b3-b771-97311241f659/12f23d7c-c260-43d3-806f-d032b54cb4ea.png)
4.  Click **Add Recipe** and add an item in the dashboard. 5.  Click **Manage Collection**. Open the collection and view the item you just added. 6.  You can also go back to the site editor and change the number of items again.  * * * ## Step 14 | Installation settings Blocks [installation settings](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/configure-blocks-installation-settings.md) determine how this app is installed for other people's websites.  1. Go back to Blocks.  2. Click on the 3 dots next to the Subscribe widget name. 3. Click **Editor Experience** and **Edit Installation Settings**. 4. Click **Installation Settings**. 5. Set it as **Not added automatically** (to a site's **Add** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/17/4ebb2302-aa03-435e-a0e3-bd984c0c82a3/429fb7ea-3a90-47d2-b8f5-bba4ef62a425.jpg)  panel). This is because people will install the main widget and get the inner widget in it. 
See screenshot ![subscribe](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7f11703043cd41a0115143436e285ed0.png)
5.  Click the **Recipes** widget. Set it to be **Added to homepage**. 6.  As a default preset, choose the **Editorial Layout**. 7.  Choose the **Mobile Layout** preset to be the default for mobile. 
See screenshot ![installation settings recipes](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3bcfcd74992b173908dd7c4949338b69.png)
8.  Click **Save**. 9. Go back to Blocks and click **Release** again to apply the installation settings.  10. To see the installation settings in action, you need to uninstall and reinstall the app.  * * * ## Step 15 | Publish your app in the Wix App Market Blocks apps can be used on your own sites and save you lots of copy-and-paste. They can also be published and sold in the Wix App market and earn money. The last thing we'll do is simulate how to publish your app, in case you want to publish your own app in the future.   1. Click **WixBlocks** \-> **App**. 2. Click **Publish App**. This takes you to [Wix Developers](https://manage.wix.com/account/custom-apps).  3. Click **Pricing**. Create a Premium plan and decide on its pricing.  4. Build your app in Blocks again.  Now your app has a premium plan. The next step is to configure what happens in Blocks according to the different plans that a user purchased. What extra abilities do you want them to have? For example, in our app, we might want the subscribing option to be disabled until a user has paid. Learn more about [adapting your app to a pricing plan](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/publish-blocks-apps-to-the-app-market/adjust-a-blocks-app-to-different-pricing-plans.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Site Plugin for the Wix Stores Product Page ## Article: Tutorial | Create a Site Plugin for the Wix Stores Product Page ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-site-plugin-for-the-wix-stores-product-page.md ## Article Content: # Tutorial | Create a Site Plugin for the Wix Stores Product Page In this tutorial, we demonstrate how to create the **Digital Sale Banner** [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Wix Stores product page. This plugin displays a small banner on digital products to let customers know this product is included in the current digital sale promotion. It is meant to be embedded in a Wix Stores [product page slot](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md#plugin-slots). The end result will look something like this: ![digital-sale-banner-plugin-on-site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_digital-sale-site.png) We'll use the following steps to build the Digital Sale Banner site plugin: 1. Create an app. 2. Design your plugin in Wix Blocks. 3. Code your plugin in Wix Blocks. > **Note:** Slots differ slightly depending on [which version of Wix Stores](https://support.wix.com/en/article/wix-stores-customizing-your-product-page) a user has on their site. This tutorial works for both versions. Learn more about building [product page site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md). ## Step 1 | Open the Wix Studio workspace and create an app You can access Wix Blocks through the Wix Studio workspace. 1. Open the [Wix Studio workspace](https://manage.wix.com/studio/). 2. Click **Custom Apps** in the left menu. 3. Click **Create New App** and then select **Build from scratch**. 4. When prompted to choose a framework, select **Wix Blocks**. Your newly-created app opens in Wix Blocks. ## Step 2 | Design your plugin’s UI Now design your plugin in Wix Blocks: 1. Open the **Widgets and Design** panel, click ![add-plugin-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_add-plugin-icon.png), and select **Add Plugin**. 2. In the **Add a plugin panel**, set up the plugin as follows: * **Plugin name**: `Digital Sale Banner Plugin` * **Which Wix app does it extend?**: `Wix Stores` * **Slots this plugin can be added to**: `product-page-details-2 (New Product Page)` and `product-page-details-2 (Old Product Page)` > **Note**: Check [Wix Stores: Product Page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md) to see the available slots. This example uses the slots for both the new version of Wix Stores product page and the old version. This ensures a user can add your app to their site with whichever version they have. 3. Click **Create**. 4. Add an image element to the plugin. Click **Add Elements** ![add-elements-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_add-elements-icon.png) on the left side of the editor, and then click **Image**. 5. Upload the image that you want to appear on the relevant product pages. Click **Change Image**. Select the image you want to use and click **Update**. 6. If you find your image is cut off, then reset the image. Select the image and click on ![settings-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_settings-icon.png). Click ![reset-image](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_reset-image.png). 7. Next, let's make sure that when the plugin is added to a site, the image stretches across the entire width of the Wix Product page slot. Select the image element. In the Inspector panel on the right, click the **Layout** tab, and set the element's width to **100%**.
See how it looks
## Step 3 | Code your plugin We now write the code for our plugin. The code checks if the product is digital and, if so, updates the slot on the page to display the digital sale image. 1. Before coding our plugin, we need to connect our plugin to the host widget (in our case, the Wix Stores product page) by implementing the [`Product plugin API`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md#plugininterface-and-productid). To do so, add the `productId` property to your plugin's public API. In the code panel, click the **Widget Public API** ![widget-api-con](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_widget-api-icon.jpeg) icon, and then click **Add New Property**.
See how it looks
2. In the **New Property** panel, name the property `productId`, and then click **Create**. 3. In the code editor, add the following import statement at the beginning of your code to use Wix Stores APIs: `import { products } from 'wix-stores.v2';` 4. Make the `$widget.onPropsChanged()` function asynchronous by adding the `async` keyword: `$widget.onPropsChanged(async (oldProps, newProps) => {` 5. Inside the `$widget.onPropsChanged()` code block, add the following line to get the current product ID from the host widget: `const currentProductId = newProps.productId;` This ID identifies the product for which the type will be checked. 6. Use the Wix Stores [`getProduct()`](https://www.wix.com/velo/reference/wix-stores-v2/products/getproduct) function to query the type of product: ```javascript const currentProduct = await products.getProduct(currentProductId); const currentType = currentProduct.product.productType; ``` 7. Check if the product type is digital and, if so, display the digital sale banner: ```javascript if(currentType === 'digital') { $w('#imageX1').show() } else { $w('#imageX1').hide() } ``` Here's the complete code: ```javascript import { products } from 'wix-stores.v2'; $widget.onPropsChanged(async (oldProps, newProps) => { const currentProductId = newProps.productId; const currentProduct = await products.getProduct(currentProductId); const currentType = currentProduct.product.productType; if(currentType === 'digital') { $w('#imageX1').show() } else { $w('#imageX1').hide() } }); ``` 8. In the top-right corner, click **Build**. When prompted to provide an app name, enter "Digital Sale Banner" and then click **Save & Continue**. 9. When prompted to select the type of version to build, select **Test version**. Your plugin is now ready for use. In the next step, you'll add your plugin to a site to see it in action. ## Step 4 | See your plugin in action Plugins that you develop are immediately available for installation on your own sites, using the Plugin Explorer in the editors. Go ahead and create a new site, and then install Wix Stores. Alternatively, use a [site template which already has Wix Stores installed](https://www.wix.com/website/templates/html/online-store). 1. Open your editor. In the left sidebar, click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_pages-icon.png), then **Store Pages**, and go to the **Product Page**. 2. On the Product page, click the page's widget, and then click the plugins ![plugin icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/tutorial-using-site-plugin-extensions-with-stores-product-pages-md_build-apps-portal_get-started_resources_tutorials_assets_plugin-icon.png) icon.
See how it looks
Your plugin should appear in the Plugin Explorer. 3. Hover over your plugin, and then click **Add**. When prompted for consent, select the checkbox and click **Agree & Add**. Once you install your plugin on a site, you can preview or publish the site to test the plugin's functionality and make sure it's working properly. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Site Plugin for the Wix Bookings Service Page ## Article: Tutorial | Create a Site Plugin for the Wix Bookings Service Page ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-site-plugin-for-the-wix-bookings-service-page.md ## Article Content: # Tutorial | Create a Site Plugin for the Wix Bookings Service Page In this tutorial, we demonstrate how to create the **Next Booking Availability** [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Wix Bookings Service page. This site plugin displays the next available date for a site visitor to book a particular service. It is meant to be embedded in the Wix Bookings [Service page slot](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-bookings/wix-bookings-service-page.md#slots). The end result will look something like this: ![next-availability-plugin-on-site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_next-availability-site.png) We use the following steps to build the Next Booking Availability site plugin: 1. Create an app. 2. Design your plugin in Wix Blocks. 3. Code your plugin in Wix Blocks. ## **Before you begin** * Make sure you're logged into your Wix account, or [create an account](https://www.wix.com/) if you don't have one yet. * If you haven't done so yet, move to the new Wix Studio workspace by [joining Wix Studio](https://support.wix.com/en/article/wix-studio-switching-to-wix-studio). ## Step 1 | Open the Wix Studio workspace and create an app You can access Wix Blocks through the Wix Studio workspace. 1. Open the [Wix Studio workspace](https://manage.wix.com/studio/). 2. Click **Custom Apps** in the left menu. 3. Click **Create New App** and then select **Build from scratch**. 4. When prompted to choose a framework, select **Wix Blocks**. Your newly-created app opens in Wix Blocks. ## Step 2 | Design your plugin’s UI We now design our plugin in Wix Blocks: 1. Open the **Widgets and Design** panel, click ![add-plugin-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_add-plugin-icon.png), and select **Add Plugin**. 2. In the **Add a plugin panel**, set up the plugin as follows: * **Plugin name**: `Next Availability` * **Which Wix app does it extend?**: `Wix Bookings` * **Slots this plugin can be added to**: `slot 1 (Service Page)` 3. Click **Create**. 4. Let's add a text element to the plugin. Click **Add Elements** ![add-elements-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_add-elements-icon.png) on the left side of the editor, and then click **Title**. 5. Next, we want to make sure that the text stretches across the entire width of the plugin. When the plugin is added to a site, we want the text to stretch across the entire width of the Wix Service page slot. Select the text element. In the Inspector panel on the right, click the **Layout** tab, and set the element's width to **100%**.
See how it looks
6. Still in the Inspector panel, click the **Design** tab, and set the text alignment to **Center align**. ## Step 3 | Code your plugin We now write the code for our plugin. The code checks the availability of a specific service for the upcoming year, and updates the text element with the next available date for a site visitor to book the service. 1. Before coding our plugin, we need to connect our plugin to the host widget (in our case, the Wix Bookings Service page) by implementing the [`BOOKINGS_SERVICE`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-bookings/wix-bookings-service-page.md#bookings_service) API. To do so, add the `bookingsServiceId` property to your plugin's public API. In the code panel, click the **Widget Public API** ![widget-api-con](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_widget-api-icon.jpeg) icon, and then click **Add New Property**.
See how it looks
2. In the **New Property** panel, name the property `bookingsServiceId`, and then click **Create**. 3. In the code editor, add the following import statement at the beginning of your code to use Wix Bookings APIs: `import wixBookingsFrontend from 'wix-bookings-frontend';` 4. Make the `$widget.onPropsChanged()` function asynchronous by adding the `async` keyword: `$widget.onPropsChanged(async (oldProps, newProps) => {` 5. Inside the `$widget.onPropsChanged()` code block, add the following line to get the current service ID from the host widget: `const serviceId = newProps.bookingsServiceId;` This ID identifies the service for which the availability will be checked. 6. Set up the date range for our availability query, with a `startRange` and an `endRange` that spans one year from the current date: ```javascript let startRange = new Date(); let endRange = new Date(); endRange.setDate(startRange.getDate() + 365); ``` 7. Use the Wix Bookings [`getServiceAvailability()`](https://www.wix.com/velo/reference/wix-bookings-frontend/getserviceavailability) API to query the availability for the specified service (`serviceId`) within the defined date range (`rangeOptions`): ```javascript const rangeOptions = { startDateTime: startRange, endDateTime: endRange }; const availability = await wixBookingsFrontend.getServiceAvailability(serviceId, rangeOptions); ``` 8. Extract the available slots from the returned object and get the first available slot's date: ```javascript const slots = availability.slots; const options = { weekday: 'long', day: 'numeric', month: 'short' }; const firstSlotDate = slots[0].startDateTime.toLocaleDateString('en-US', options); ``` 9. Change our text element to display the message "Next availability:" followed by the formatted date of the first available slot: ```javascript $w('#text1').text = "Next availability: " + firstSlotDate; ``` Here's the complete code: ```javascript import wixBookingsFrontend from 'wix-bookings-frontend'; $widget.onPropsChanged(async (oldProps, newProps) => { const serviceId = newProps.bookingsServiceId; let startRange = new Date(); let endRange = new Date(); endRange.setDate(startRange.getDate() + 365); // one year from now // get the service availability for the upcoming year const rangeOptions = { startDateTime: startRange, endDateTime: endRange }; const availability = await wixBookingsFrontend.getServiceAvailability(serviceId, rangeOptions); const slots = availability.slots; // get the date of the first available slot const options = { weekday: 'long', day: 'numeric', month: 'short' }; const firstSlotDate = slots[0].startDateTime.toLocaleDateString('en-US', options); $w('#text1').text = "Next availability: " + firstSlotDate; // set the text element's content }); ``` 9. In the top-right corner, click **Build**. When prompted to provide an app name, enter "Booking Availability" and then click **Save & Continue**. 10. When prompted to select the type of version to build, select **Test version**. Our plugin is now ready for use. In the next step, we add our plugin to a site to see it in action. ## Step 4 | See your plugin in action Plugins that you develop are immediately available for installation on your own sites, using the plugin explorer in the editors. Go ahead and create a new site, and then install Wix Bookings. Alternatively, use [this site template](https://manage.wix.com/edit-template/from-intro?debug=all&originTemplateId=85dec86a-afc8-41cb-8a86-c12d4a525fe5), which already has Wix Bookings installed. 1. Open your editor. In the left sidebar, click **Pages** ![pages-icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_pages-icon.png), and go to **Booking Pages > Service Page**. 2. On the Service page, click the page's widget, and then click the Plugins ![plugin icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/plugin-tutorial-next-availability-md_build-apps-portal_get-started_resources_tutorials_bookings-plugin-tutorial_images_plugin-icon.png) icon.
See how it looks
Your plugin should appear in the plugin explorer. 3. Hover over your plugin, and then click **Add**. When prompted for consent, select the checkbox and click **Agree & Add**. Once you install your plugin on a site, you can preview or publish the site to test the plugin's functionality and make sure it's working properly. > **Note**: The Service page lets you reposition the plugin within the page's layout by [reordering the Service page sections](https://support.wix.com/en/article/wix-bookings-customizing-your-service-pages). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Overview ## Article: Overview ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/overview.md ## Article Content: # Tutorial | Build an eCommerce Business Solution > [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-1-create-a-custom-wix-app.md) As a Wix app developer, you can build a full-scale solution for a business, similar to Wix business solutions such as Bookings or Restaurants. If your business solution requires eCommerce capabilities, you can integrate it with the [Wix eCommerce platform](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/overview/what-is-the-wix-e-commerce-platform.md). This lets you take advantage of Wix’s built-in eCommerce infrastructure, instead of spending the time and effort to create eCommerce functionality yourself. This series of articles walks you through how to build an example business solution that integrates with Wix eCommerce. Though the example is simple, you can use the tools and structure shown throughout the tutorial to build more complex solutions for any kind of business. As part of the tutorial, we’ll build an app for selling poems. This is a multistep process that involves the following major parts: - [Step 1 | Create a Custom Wix app](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-1-create-a-custom-wix-app.md) - [Step 2 | Create a Catalog Database in Blocks](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md) - [Step 3 | Create an Item Page in Blocks](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md) - [Step 4 | Create a Dashboard Page to Manage the Catalog](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md) - [Step 5 | Implement a Self-Managed Catalog Service Plugin](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md) - [Step 6 | Test your Business Solution on a Site](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md) Each article in the series covers a separate step. If you complete the full tutorial, you’ll have a simple business solution that implements a catalog and provides frontend functionality to both Wix users and their customers. You can use this basic app as a starting point for developing your own business solution. ## Before you begin Before getting started, make sure that: - You install [Node.js](https://nodejs.org/en/download/) (v20.11.0 or higher). - You install [npm](https://www.npmjs.com/package/npm) or [yarn](https://www.npmjs.com/package/yarn). - You’re logged into your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). **Next up:** [Step 1 | Create a Custom Wix app](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-1-create-a-custom-wix-app.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Self-Managed App ## Article: Create a Self-Hosted App ## Article Link: https://dev.wix.com/docs/build-apps/get-started/quick-start/create-a-self-managed-app.md ## Article Content: # Tutorial | Create a Self-Managed App In this tutorial, you'll create an app with a simple [dashboard page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) using a [self-managed](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting-for-wix-apps.md) iframe. ## Before you begin * Make sure you're logged into your Wix account, or [create an account](https://www.wix.com/) if you don't have one yet. * If you haven't done so yet, move to the new [Wix Studio workspace](https://manage.wix.com/account/custom-apps) by [joining Wix Studio](https://support.wix.com/en/article/wix-studio-switching-to-wix-studio). ## Step 1 | Create an app in the Custom Apps page of your Wix Studio workspace We'll start off by creating a new app. 1. In the [Custom Apps page](https://manage.wix.com/account/custom-apps), go to **My Apps**, and then click **Create New App**. 2. Select **Build from scratch**. 3. Select **Self-Managed**, and then click **Get Started**. ![choose-app-framework](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f834d79429fff1627157c1cb4071fabe.png) A new app is created in the Custom Apps page. ## Step 2 | Add a dashboard page extension We'll now add a dashboard page extension in your app's dashboard. This extension uses an iframe to embed a web page in the dashboard. We've already deployed a web page on an external server for you to use in this tutorial. When configuring the extension, you'll register the URL of this web page. 1. In the left sidebar, under **Develop**, select **Extensions**, and then click **Create Extension**. A panel opens, showing the available extension types. 2. Find the **Dashboard page** extension and click **Create**. 3. Fill in the following configuration data: * **Name**: `Demo page` * **iframe URL**: `https://www.quickstartselfhostedapp.com/` 4. Click **Save**. ![iframe-url](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f2f1e4ce6ddc5fee7b50bb7e7e3b52dc.png) You’ve finished setting up the dashboard page extension and can now install it on a site to see it in action. ## Step 3 | Install your app on a site Wix provides a free Premium development site so you can easily install your app and see it working. In this step, you'll create a development site, install your app on the site, and try it out. 1. In the top right corner of your [app's dashboard](https://manage.wix.com/account/custom-apps), click **Test App** and select **Test on dev site**. 1. Select an existing development site or click **+ Create Dev Site** to create a new site. Select the editor and the Wix Business Solution you want to use and click **Create Dev Site**. 1. Click **Test App**. Wix installs your app and opens the site in a new tab. You can set which site page opens in your [app settings](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fapp-settings). If you don't set a page, the site editor opens by default. Your app is now installed on the development site. You can see the external web page embedded in the dashboard. ![self-hosted-iframe](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84c1722c069c39693ea2316875639fac.png) ## Next steps You now have a fully working app that can be installed on Wix sites. Take some time to play around in your app's dashboard to explore more features that you can add to your app. Use the following resources to continue building your app: * [Wix Design System](https://wixdesignsystem.com): Learn how to use Wix's React components for a cohesive user experience consistent with Wix's design standards. * [Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md): Learn how to integrate your app with Wix to transfer data between your app and the site it's installed on and extend Wix business solutions, such as Wix eCommerce or Wix Bookings. * [Extension catalog](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md#extension-catalog): Learn about other extensions you can add to your app. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Set Up an App With the App Dashboard ## Article: Tutorial | Set Up an App With the Wix Developers Center ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-app-dashboard.md ## Article Content: # Tutorial | Set Up an App with the App Dashboard This tutorial shows you how to set up a new Wix app using the app dashboard. The tutorial shows you how to create, configure, and test an externally hosted "Product of the Day" app that can be installed on a Wix site. The app lets site admins offer a daily discount on a product they choose. Upon downloading the app, the site gains access to the following features: + **In the site dashboard:** A dashboard page where a site admin can choose a product from their catalog to discount. + **In the site:** An automated chat message offering site visitors a discount coupon for the selected product. The dashboard page extension is built with [React](https://react.dev/) and the server with [Express.js](https://expressjs.com/). We provide all the code you need for this app in the [example apps GitHub repository](https://github.com/wix-incubator/wix-developers-example-apps). Regardless of the technologies you use to build your app, you can take similar steps to integrate it with the app dashboard. The end result will look like this: ![App preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/68dedb33ad087254b6b95eed8fa8132f.png) We'll take the following steps to implement the Product of the Day app: 1. Clone the app repository. 1. Create an app from the [Custom Apps page](https://manage.wix.com/account/custom-apps) in your Wix Studio workspace. 1. Configure permissions for your app. 1. Set up the dashboard page extension. 1. Set up the app's backend. 1. Test the app out locally. ## Before you begin Before getting started, make sure that: + You have [Node.js](https://nodejs.org/en/download/) (v16.4 or higher). + You're logged into your Wix Studio account. If you don't already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). ## Step 1 | Clone the app repository This tutorial uses a pre-built app we've saved in the [example apps GitHub repository](https://github.com/wix-incubator/wix-developers-example-apps). To clone the repository to your computer, open the terminal and run the following command from the folder you want to clone the repository into: ```bash git clone https://github.com/wix/wix-developers-example-apps.git ``` Then navigate to the folder containing the Product of the Day app: ```bash cd wix-developers-example-apps/product-of-the-day ``` Within this folder you can find both the backend app server (in the `server` folder) and the frontend dashboard component (in the `client` folder). ## Step 2 | Create an app in the Custom Apps page of your Wix Studio workspace The Custom Apps page in your Wix Studio workspace is the place for managing Wix apps you develop. Follow these steps to create a Wix app that we'll connect to the external servers. 1. Go to [Wix Studio](https://manage.wix.com/account/custom-apps) and sign in. 1. If this is your first Wix app, click **Start Building**. If you already have an app, click **Create New App** in the top-right corner. This opens your new app's dashboard: ![New app page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/af428af6b08fa3c35854076096ccf64d.png) 1. Click the default name generated for your app, and enter a new one, such as "Product of the Day". ## Step 3 | Configure permissions for your app This app uses Wix [REST APIs](https://dev.wix.com/docs/rest.md) to access and manage Wix site data, requiring specific permissions to access each endpoint. To ensure functionality, your app needs these permissions from any site using it. This section guides you through setting up your app to request these permissions upon installation. ### Find required permissions To find the permissions that your app requires: 1. Go to the [Wix REST API reference documentation](https://dev.wix.com/docs/rest.md). 1. Find the specific endpoints that your app calls. The Product of the Day app calls the following endpoints: * [Query Products](https://dev.wix.com/docs/rest/business-solutions/stores/catalog/query-products.md): To retrieve information about products in the site's catalog. * [Create a Coupon](https://dev.wix.com/docs/rest/business-management/marketing/coupons/coupons/create-a-coupon.md): To generate a discount coupon for the product the site owner chooses. * [Send Message](https://dev.wix.com/docs/rest/crm/communication/inbox/messages/send-message.md): To send site visitors a message with the coupon in the Wix Chat widget. 1. In each endpoint reference, look for **Permission Scopes**: ![Permission Scopes in API Ref](https://wixmp-833713b177cebf373f611808.wixmp.com/images/24a89f8c82beaf1606b5627b21f82ad7.png) The Product of the Day app requires the following permission scopes: | Endpoint | Required permissions | |--|--| | Query Products | Read Products | | Create a Coupon | Manage Coupons (with the identifier `SCOPE.DC-COUPONS.MANAGE-COUPONS`) | | Send Message | Manage Inbox Messages | ### Add permissions To add permissions for the app to request upon installation: 1. Go to **Permissions** in your app's dashboard. 1. For each permission scope: 1. In the left sidebar, click **Add Permissions**. 1. Enter the permission scope's name in the **Search by name or ID** field. 1. Check the permission scope's checkbox under **Choose Permission Scopes**. 1. Click **Save**. Now, when a site owner installs the app, Wix prompts them to grant the necessary permissions. ## Step 4 | Set up the dashboard page Every Wix site has a dashboard where site admins manage their site and business. Our app integrates with this dashboard by adding a new page where site admins can select a product of the day and apply discounts. The dashboard page is built with [React](https://react.dev/). This section guides you through adding the dashboard page to your app. ### Run a local server Our app includes two servers: one for the dashboard page UI (in the `client` folder) and one for the app's backend business logic (in the `server` folder). To run the local development server for the dashboard page UI: 1. Navigate to the `client` folder: ```bash cd client ``` 1. Install the dependencies: ```bash npm install ``` 1. Start the local server for the dashboard page: ```bash npm run start:secure ``` This runs the local dashboard page server and opens your browser to `https://localhost:3000`. > **Note:** If you encounter a security warning, click **Advanced** and then **Proceed to localhost (unsafe)**. Now, you can see the dashboard component open in your browser: ![Dashboard component](https://wixmp-833713b177cebf373f611808.wixmp.com/images/bd91f9cf19653bd26546f60197e54077.png) > **Note:** Make sure to keep the local dashboard page server running for the remainder of this tutorial. ### Add a dashboard page To set up your app to include the dashboard page: 1. In the left sidebar, click **Extensions**. 1. Click **Create Extension**. 1. Check the **Dashboard** checkbox. 1. In the **Dashboard Page** card, click **Create**. ![Create Dashboard Page extension](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f310b721d4ff2cdcd7e2be55d1da3bfb.png) 1. Under **Page info**, in the **iFrame URL** field, enter the address for your local dashboard page server: `https://localhost:3000/`. ![Enter local host address](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0fe0cf3dcd2da0bced52b85168ccecd4.png) 1. Under **Sidebar configuration**, in the **Page name** field, enter a name for your dashboard page. This name is shown in the dashboard sidebar menu. ![Name the dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8d44ce51d6b61c767b172c0a24f9f520.png) 1. Click **Save**. Your app is set to add a dashboard page upon installation on a site and load it from the specified URL. ## Step 5 | Set up the backend Our app contains business logic in our backend server. This section guides you through setting up the backend, including authentication for your app. ### Configure environment variables To configure environment variables for authentication: 1. In the `product-of-the-day/server` folder, create a file named `.env` with the following placeholder values: ```env APP_ID= APP_SECRET= WEBHOOK_PUBLIC_KEY="" ``` > **Note:** Only the webhook public key value needs to be enclosed in quotes. 1. In the left sidebar, click **OAuth**. 1. In the `.env` file created in the first step, replace `` with the **App ID** and `` with the **App Secret Key**. ![App ID and App Secret Key on OAuth page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/35a82b2e0b808651dfa53c079f9fb74c.png) ### Add a webhook to your app Webhooks deliver real-time notifications about events in your app, other apps, or the Wix site. For this app, we'll add the [Message Sent To Business](https://dev.wix.com/docs/rest/crm/communication/inbox/messages/message-sent-to-business.md) webhook, which notifies the app whenever a site visitor sends a message in the Wix Chat widget. To add a webhook: 1. In the left sidebar, click **Webhooks**. 1. Click **Add Webhook**. 1. Under **API Category**, select **Inbox**. 1. Check the box next to **Message Sent To Business**. 1. In the **Callback URL** field, enter a temporary dummy URL, such as `https://www.test.not`. We'll replace this URL after activating the backend app server. 1. Click **Save**. This takes you back to the **Webhooks** page. 1. Under **Public key**, click **Open**. 1. Click **Copy Key**. 1. In your `.env` file, replace `` with the key value: ```env WEBHOOK_PUBLIC_KEY="" ``` ### Start the backend server To successfully set up our app, the server for the dashboard page and the backend server need to be running at the same time. To start the backend server: 1. Open a new terminal window, and navigate to `product-of-the-day/server`: ```bash cd product-of-the-day/server ``` 1. Install the app's dependencies: ```bash npm install ``` 1. Start the local app server: ```bash npm run start ``` This runs the local app server and displays several important URLs: ![App server URLs](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4afbdd45000ca7d82999ed9769836799.png) * `RedirectUrl`: Endpoint Wix must redirect to after the user authenticates. * `AppUrl`: Main entry point for the app's backend functionality. * `Message received webhook`: Used for receiving callbacks from Wix. 1. Copy the `RedirectUrl`, `AppUrl`, and `Message received webhook` and keep them available for the next steps. > **Note:** Make sure to keep the backend server running for the remainder of this tutorial. ### Configure OAuth Wix uses [OAuth 2.0](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-oauth.md) to authorize apps to access our APIs and receive webhooks. To configure OAuth: 1. In the left sidebar, click **OAuth**. 1. Under **URLs**: * Enter the `AppUrl` from the previous step into the **App URL** field. * Enter the `RedirectUrl` from the previous step into the **Redirect URL** field. 1. Click **Save**. ### Configure the webhook To make sure your app receives the webhook: 1. In the left sidebar, click **Webhooks**. 1. In the **Webhook List**, next to the **Message Sent to Business** webhook, click the ellipsis ![Ellipsis button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1b4b4fa19d767290c115c5d07c26e3fe.png). Then, click **Edit**. 1. In the **Callback URL** field, enter the `Message received webhook` URL from the previous step. 1. Click **Save**. Your app is now properly configured and ready to test. > **Note:** Every time you run the Product of the Day app server, it generates new URLs for the app. If you reset the server, repeat this step to configure the updated URLs. ## Step 6 | Test the app Wix provides a free Premium development site so you can easily install your app and see it working. In this step, you'll create a development site, install your app on the site, and try it out. ### Set up a development site To set up a [development site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app-on-a-premium-site.md): 1. In the top right corner of your [app's dashboard](https://manage.wix.com/account/custom-apps), click **Test App** and select **Test on dev site**. 1. Click **+ Create Dev Site** to create a new site. Select your preferred editor and both **Wix Stores (Catalog v3)** and **Wix Stores (Catalog v1)** business solutions and click **Create Dev Site**. 1. Confirm that the new site you just created is selected. Note that the automatically generated name begins with **Dev Site**. If it isn't, select it, and click **Test App**. Wix installs your app and opens the site in a new tab. You can set which site page opens in your [app settings](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fapp-settings). If you don't set a page, the site editor opens by default. 1. In your **Sites** page, find the development site you just created. Hover over the development site and click **Select & Edit Site**. This opens the site's dashboard. 1. Click **Design Site**. This opens the site's editor. 1. Add a product page to the site: 1. In the left sidebar, click **Pages** ![Pages & Menu button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/906ffc254e57c7a3a3237531263f0bbe.png). 1. In the **Store Pages** tab, select **Product Page**. 1. Edit the page as desired. 1. Back in the editor, on the top right, click **Publish**. Your development site is now live. ### Install the app To install the app on your development site: 1. Return to your app's dashboard. 1. Click **Test Your App**, then click **Preview listing**. This takes you to a preview listing page for your app in the Wix App Market. 1. Click **Test on Dev Site**. 1. Next to the development site you just created, click **Select**. 1. Wix now requests the site permissions your app requires. Click **Agree & Add**. The Product of the Day app is now installed on your development site. ### Try out the dashboard page To test the app, we'll start by trying out its dashboard page, where we can choose a product of the day and apply a discount: 1. Open the development site's [dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2F). Your app now appears in the left dashboard sidebar under the name you entered earlier. Click your app's name in the sidebar. ![Product of the Day dashboard sidebar item](https://wixmp-833713b177cebf373f611808.wixmp.com/images/72c691b110373235aabc3e67a4679235.png) This opens your app's dashboard page. 1. Click **Choose Product**. ![Product of the Day dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/289b5d3668fac8bcaae2fbd0be6132d0.png) This opens a menu to select a product. 1. Your development site's catalog is filled with example products with the name "I'm a product". To make these products appear, type "I'm" in the **Search by product name** field. Then, select a product and click **Done**. ![Select a product](https://wixmp-833713b177cebf373f611808.wixmp.com/images/134684dc89a1be5fb02b11c052a39c5d.png) 1. Adjust the percentage **Discount** to apply to the product, and click **Update**. ![Apply discount](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3942717bbd157db850a5eb478c37590b.png) You've now selected a product of the day and applied a discount to it. ### Try out the site Now let's see what the experience is like for a site visitor. 1. Open your development site's editor, hover over **Publish** in the top right, then click **View Site** to open your live site. 1. In your live site, click the chat icon ![Chat Icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b6ec7ab732ea6923688174ee1a8edddb.png) on the bottom right to open the chat widget. 1. In the chat widget, type any text. For example, "Hello". 1. After a few moments, you'll receive a response in the chat widget, offering you a discount on the product of the day: ![Chat widget with product of the day discount](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6efbbd6d21b761395a625f7ec648daa8.png) 1. The chat response contains a coupon code and a link. Copy the coupon code, and click the link to go to the product page for the product of the day. 1. In the product page, click **Add to Cart**. Then, to go to the cart page, click **View Cart**. 1. Click **Enter a promo code**, paste the coupon code you copied, and click **Apply**. ![Enter promo code](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e61c1d69848e235938175f98a71483bc.png) Your **Order Summary** now incorporates the Product of the Day discount: ![Order summary with discount applied](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1961fa594846bc2ccdacd586b4107164.png) ## Step 7 | Customize the app The Product of the Day app is now working! To learn more about how the app works, take a look around the code files in the [GitHub repo](https://github.com/wix-incubator/wix-developers-example-apps). As an exercise to help familiarize yourself with app code a little more, you can add additional functionality to this app. For example, edit the code so that each site visitor can only receive one discount coupon per day. To do this, edit `product-of-the-day/server/src/services/ProductOfTheDayService.js`, and add the lines of code marked as `ADDED` below in the comments: ```js const dayjs = require('dayjs'); // ADDED: Import the Day.js library for date formatting. const _ = require('lodash'); // ADDED: Import the Lodash library for working with objects. class ProductOfTheDayService { // ... async sendCouponOfTheDay(instanceId, participantId) { const today = dayjs().format('DD/MM/YYYY'); // ADDED: Store the current date. const productOfTheDayData = await this.productOfTheDayDao.getBy(instanceId) const conversationResult = await this.wixInboxApis.getConversation(instanceId, participantId) const conversationId = conversationResult?.conversation?.id const coupon = await this.couponsDao.getBy(conversationId, today); // ADDED: Retrieve existing coupon for this conversation with the current date, if there is one. if(_.isEmpty(coupon)){ // ADDED: Send a coupon only if no existing coupon was retrieved for this conversation with the current date. const query = {"filter":`{\"id\": {\"$eq\": \"${productOfTheDayData.productId}\"}}`} const [productData, couponData] = await Promise.all([ this.storesApis.queryProducts(instanceId, query), this.couponsApis.createCoupon(instanceId, productOfTheDayData.productId, productOfTheDayData.discountPercentage) ]) this.couponsDao.save(instanceId, conversationId, today, couponData); // ADDED: Save the coupon in the coupon database. const message = this.generateCouponMessage(conversationId, couponData, productData?.products, productOfTheDayData.discountPercentage) console.log('sending coupon...'); return this.wixInboxApis.sendMessage(instanceId, message) } // ADDED } // ... } module.exports = { ProductOfTheDayService } ``` With this adjustment, the app now checks whether a discount coupon was already created for the site visitor on the current date. It only sends a coupon if one wasn't already sent. ## What next? Now that you've learned how to set up a pre-made, externally hosted Wix app, you can start thinking about building your own apps. Here are a few resources to get you started: + Browse the [Wix App Market](https://www.wix.com/app-market) to get inspiration from other apps. + Learn about [self-managed apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting-for-wix-apps.md). + Get to know our [REST API](https://dev.wix.com/docs/rest.md) and our [JavaScript SDK](https://dev.wix.com/docs/sdk.md). + Read our [guidelines](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/app-market-guidelines.md) for submitting an app to the Wix App Market. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Self-hosted Custom Shipping Rates App ## Article: Tutorial | Create a Self-hosted Custom Shipping Rates App ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-self-hosted-custom-shipping-rates-app.md ## Article Content: # Tutorial | Create a Self-Managed Custom Shipping Rates App In this tutorial, we’ll show you how to create an app similar to our [Custom Shipping Rates app template](https://dev.wix.com/apps-templates/template?id=0580022b-625e-4467-9f78-28cc3e618483) from scratch. When a user installs the app on their site, it adds a dashboard page that allows them to create custom shipping rates. These rates are added to customers’ transactions in the cart and checkout pages. You’ll gain experience with the following Wix tools: * [App dashboard](https://manage.wix.com/account/custom-apps) * [Wix Design System](https://www.wix-pages.com/wix-design-system/?path=/story/getting-started--about#What_can_you_build?) * [Wix SDKs and APIs](https://dev.wix.com/docs/sdk.md) * [Wix Shipping Rates Service Plugin](https://dev.wix.com/docs/sdk/backend-modules/ecom/service-plugins/shipping-rates/introduction.md) At the end of the tutorial, you’ll have a basic app that calculates custom shipping rates and adds them at checkout. You can then extend the app however you like, either by adding your own logic or following the code in the [Github repo](https://github.com/wix/app-template-custom-shipping-rates). We’ll use the following steps to build the shipping rates app: 1. Set up the app in the Custom Apps page of your Wix Studio workspace. 2. Bootstrap a Next.js app with `create-next-app`. 3. Set up the Wix SDK. 4. Write the code for the main app logic. 5. Deploy your app. 6. Test your app out on a dev site. ## Before you begin Before getting started, make sure that: * You are signed into your [Wix account](https://manage.wix.com/account/custom-apps). * You have [Node.js](https://nodejs.org/en/) 18.17 or higher installed. ## Step 1 | Set up your app in the Custom Apps page In the [Custom Apps page](https://manage.wix.com/account/custom-apps), click **Create New App** > **Build from scratch** to create a new app. Wix generates a Wix app ID, secret, and JWT key that you will need later when configuring your app to work with the Wix SDK packages. Give your app a name, but you don’t need to configure anything else for now. ## Step 2 | Create the project with `create-next-app` This step configures a full Next.js project in your local directory. We’ll set up our project in two steps: 1. Create the Next.js app with [create-next-app](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2. Set up the folder structure and add the files necessary to build the basic app. ### Create the app We’ll create our project using the interactive option in `create-next-app`. To create the project: 1. On the command line, navigate to the directory where you want to save your project. 2. Run the following command: ```bash npx create-next-app@latest ``` 3. Give your project a name when prompted. For example: `custom-shipping-rates`. 4. The tool will present a list of dependencies. Choose `yes` for the following: * TypeScript * ESLint * `src/` directory * App Router Once you select your dependencies, `create-next-app` will create your new application. Now you have a new project in your selected directory. The project has a basic folder structure that we are going to build on next. ### Set up the project structure In these next few steps, we’ll walk through the folder set up. We’ll also add several necessary files. 1. In this tutorial, we develop our app using the Next.js [App Router](https://nextjs.org/docs/app/building-your-application/routing#the-app-router). We start by creating a folder in `src/app/` to work with the App router. 2. We need to define the [`getShippingRates()`](https://dev.wix.com/docs/sdk/backend-modules/ecom/service-plugins/shipping-rates/get-shipping-rates.md) handler function from the [Shipping Rates service plugin](https://dev.wix.com/docs/sdk/backend-modules/ecom/service-plugins/shipping-rates/introduction.md), which is called when the service plugin endpoint receives a request. This requires us to set up an [API route handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) in our app. Create a new directory `src/app/api/shipping-rates/v1/getRates/`. Next.js uses this folder path to determine the endpoint path for your Get Shipping Rates implementation. 3. In addition to handling service plugin requests, our app will also provide a dashboard page where a user can customize their shipping rates. Therefore, we also need to set up a [route](https://nextjs.org/docs/app/building-your-application/routing/pages) for the dashboard. Create a folder `src/app/dashboard/`. Inside this folder, add two items: 1. A file called `page.tsx`. This file provides the Next.js route for the app’s dashboard page. 2. A subfolder called `parts`. This folder will contain the different dashboard components that we create later. For now, you can leave it empty. 4. In `src/app`, create these folders: 1. `actions` 2. `client-hooks` 3. `types` 4. `utils` You can leave these folders empty for now. We will add the files and code we need later. 5. In the root directory of your project, add a file called `.env.local`. In this file, we’ll add the environment variables we need for local development. > **Note:** > Add the `.env.local` file to `.gitignore` so it is not pushed to the origin. Once you’ve added everything, your directory structure should look something like this: ``` └── .env.local └── src └── app └── actions └── api └── shipping-rates └── v1 └── getRates └── client-hooks └── dashboard └── page.tsx └── parts └── types └── utils ``` > **Note:** > Files that were automatically generated by `create-next-app` are not included here. Now that our project has the correct folder structure, we can set up the environment to work with the Wix SDK. ## Step 3 | Set up the Wix SDK We need to do a few things to make sure our app can work with the Wix platform: 1. Install the Wix SDK package. 2. Set environment variables that will be used locally for Wix Client authorization. 3. Create the Wix Client. ### Install Wix SDK Navigate to the root folder of your project on the command line, and run the following command: ```bash npm install @wix/sdk ``` This installs the Wix SDK package, which provides methods to let your app authenticate and communicate with the Wix platform. ### Set local environment variables Connect the local environment to the app you created in the Custom Apps page as follows: 1. Paste the following code into the `.env.local` file: ```bash WIX_APP_ID= WIX_APP_SECRET= WIX_APP_JWT_KEY= ``` 2. Go to your app and click on **OAuth**. Copy the App ID at the top of the page. Paste the App ID as the value for `WIX_APP_ID` in `.env.local`. 3. Next to the **App Secret Key** field, click the eye icon to show the secret key. Copy and paste it in `.env.local` as the value for `WIX_APP_SECRET`. 4. To get the value for the public key, go to **Webhooks** and scroll to the bottom of the page to the **Public Key** section. Click **Open** to show your public key. Copy the public key. > **Note**: > In order to view your public key, you first have to add a webhook. You have two options to paste the value of the public key for the value of `WIX_APP_JWT_KEY`: * Paste the value exactly as it appears in the app dashboard, and surround it with quotation marks. * Base64 encode the public key, and paste the value without quotation marks. We strongly recommend this method, as some deployment platforms don’t support multi-line environment variables. You can use an online encoder to base64 encode your public key. Your `.env.local` file should look similar to this when you are done (we used base64 encoding): ```bash WIX_APP_ID=9d8fc271-ec91-4f33-8151-75f9453be347 WIX_APP_SECRET=fa10735e-3139-4e91-a3bb-4aba7f151ac8 WIX_APP_JWT_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwQjhvYzZEK1BWTDVSV0ZXWXR1SwpBSnJ2Z0hvd2hCOGw0UURHNXBLVTVBOUF5bzRPMktyeG9pWU05NzlwRmg0bUlQdW9llut4QjNnMFpWVUpza0tQCmQrc29tR1JyNlc2emNOWW5VbDgxVVFGZ3MyeTlOekQzQUFnQnVFQlhJOFpqbTRnYUlVTm10LzZCdVM0THRhU2cKV0E3Y0dLUcFLY2VOVjZTUlBFT1hBUWlrL0R0bmRjTUhQNkZzclo5UDV6R0ZYTlpQZ0k4aHVBSm5OVXJzUG5VQwpSaGlSMGowcDBJcG9MOEkwSWxhZVBzWjFUTEpIVlkxMjR2aDREUktkbHZTNS9YYkxwRVJ1Z3M3QkFXc0l5L2FLCjJ3dG80MDZkRHphM0l3TWo3WW5QaXpnVW91ODRNTTgvQVBVRjFvX0laclBaTmUxblIzcDNoSjNGT2JPT0tEbmoKcVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t ``` Now that we’ve set our local environment variables, we can use them to create a Wix Client. > **Note:** > You can also use the local environment to [run your code locally](#run-the-app-locally). ### Create a Wix Client with AppStrategy The [Wix Client](https://dev.wix.com/docs/sdk/core-modules/sdk/wix-client.md) allows your app to authenticate and communicate with the Wix platform. In this tutorial, we’ll set up two separate clients for our app: * A client that authenticates as an app instance. This will be used to handle app-level logic such as receiving service plugin requests and handling webhooks. * A client that authenticates on behalf of a Wix user. This client works with the dashboard. We will create this client later in the tutorial. In this step, we’ll add the code to set up the first client that authenticates as an app instance. In the `src/app/utils` directory, add a file called `wix-sdk.app.ts`. In this file, paste the following code: ```javascript import { AppStrategy } from '@wix/sdk/auth/wix-app-oauth'; import { createClient } from '@wix/sdk/client'; export const wixAppClient = createClient({ auth: AppStrategy({ appId: process.env.WIX_APP_ID!, appSecret: process.env.WIX_APP_SECRET!, publicKey: process.env.WIX_APP_JWT_KEY, }), }); ``` #### Understanding the code **Lines 1-2**: Imports [`AppStrategy`](https://dev.wix.com/docs/sdk/core-modules/sdk/app-strategy.md) and `createClient()` from the Wix SDK. Both are necessary to set up our app’s Wix Client. **Lines 4-10:** Creates a Wix Client by passing `AppStrategy` as the `auth` parameter. Inside `AppStrategy` we set the Wix app ID, secret key, and public key from the environment variables in `.env.local`. Now we can import the Wix Client to our code, which we’ll add next. ## Step 4 | Add the code Now we get to the fun part–the code! Note that we won’t cover every file in the [full code](https://github.com/wix/app-template-custom-shipping-rates) repo, but you can look at [this table](#table-of-files) that summarizes what each file does. Our goal is to walk you through the app's core logic, so you can expand on it later yourself. There are two major actions that the app performs: * Returns shipping rates data when the Get Shipping Rates endpoint receives a request from a Wix site. * Displays shipping rates data on a dashboard page where a site admin can edit them, and saves any changes. We’ll add the code in three steps: 1. Add the supporting code that needs to be imported to the rest of the app. 2. Add the logic for receiving and responding to requests. 3. Add the dashboard code. Once you’ve completed these steps, you’ll be able to install your app on a site, view the dashboard, and handle [shipping rates](https://dev.wix.com/docs/sdk/backend-modules/ecom/service-plugins/shipping-rates/introduction.md) requests from Wix. ### Add supporting code We’ll start by adding code that will be used widely across the app, such as getter and setter methods and type interfaces to define our shipping data. #### Create shipping rates data types We’re going to create several types to make our code more flexible, especially for the dashboard code. 1. Navigate to the `types` directory that we created earlier, and in this folder add a file called `app-data.model.ts`. 2. In this file, add the following two enums: ```javascript export enum ShippingUnitOfMeasure { NUM_OF_ITEMS = 'NUM_OF_ITEMS', WEIGHT_IN_KG = 'WEIGHT_IN_KG', WEIGHT_IN_LB = 'WEIGHT_IN_LB' } export enum ShippingMethodType { STANDARD = ‘STANDARD’, EXPRESS = ‘EXPRESS’ } ``` The first enum gives options on how to determine shipping rates, either by the number of items in the customer’s cart, or by weight in kilograms or pounds. The second enum gives two options for shipping, standard or express. 3. Because our app uses Typescript, which is strongly typed, we need to create interfaces that define the structure of the types for our shipping costs and data. Below the two enums, add the following two interface definitions: ```javascript export interface ShippingCosts { first: number; second: number; thirdAndUp: number; } export interface ShippingAppData { shippingMethods: { code: string; title: string; type: ShippingMethodType; unitOfMeasure: ShippingUnitOfMeasure; costs: ShippingCosts; }[]; } ``` The first interface defines three number fields. This lets users set the price on each item that a customer orders. This field is an important part of our dashboard. The second interface uses the previously defined enums and interface to define shipping rate data type. Notice that the interface contains an array of shipping methods. This lets you define multiple shipping methods in your app. The types we define here are specific to this application. You can edit them as necessary for your app later. #### Write setter and getter methods For the purposes of this tutorial, we define a default ShippingAppData object, and create basic getter and setter methods that retrieve and set values for that default object. 1. Navigate to the `actions` directory and create a file called `app-data.ts`. 2. At the top of the file, import the types we created in `src/app/types/app-data.model.ts`: ```javascript import { ShippingAppData, ShippingMethodType, ShippingUnitOfMeasure } from '@/app/types/app-data.model'; ``` 3. Now create the default shipping app data object by adding this code: ```javascript let defaultAppData: ShippingAppData = { shippingMethods: [ { code: 'example-shipping-rate-standard', title: 'Standard Delivery', type: ShippingMethodType.STANDARD, unitOfMeasure: ShippingUnitOfMeasure.NUM_OF_ITEMS, costs: { first: 5, second: 2, thirdAndUp: 1, }, }, { code: 'example-shipping-rate-express', title: 'Express Delivery', type: ShippingMethodType.EXPRESS, unitOfMeasure: ShippingUnitOfMeasure.NUM_OF_ITEMS, costs: { first: 10, second: 4, thirdAndUp: 2, }, }, ], }; ``` Our default object contains two different methods in its shipping methods array: one for standard and one for express delivery. As you’ll see later, this will be reflected in the dashboard page. Notice that for each element, we use the shipping method type and shipping unit of measure enums that we defined in `types/app-data.model.ts` to set the shipping type and rate. For both shipping methods, we apply the rates per item. 4. Below the default object code, add the getter and setter methods: ```javascript export async function getShippingAppData(): Promise { return defaultAppData; } export async function setShippingAppData(data: ShippingAppData): Promise { defaultAppData = data; } ``` For this tutorial, we store our shipping data within this object in the app itself. This allows us to write very basic getter and setter methods. This is done for simplicity. When you create your own app, create getter and setter methods that implement logic to fetch shipping rate data from your database. The code [here](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/actions/app-data.ts) can help you get started. We can now export the methods and types we’ve created to other parts of our app. ### Handle shipping rates requests Next, we’ll add the code to implement a [Get Shipping Rates endpoint](https://dev.wix.com/docs/rest/business-solutions/e-commerce/service-plugins/shipping-rates-integration-service-plugin/get-shipping-rates.md) with the required URL path. In the `src/app/api/shipping-rates/v1/getRates/` directory that we created earlier, add a file called `route.ts`. We’ll put all the logic for receiving and responding to requests in this file. Copy and paste the code below into `route.ts`: ```js import { wixAppClient } from '@/app/utils/wix-sdk.app'; import { getShippingAppData } from '@/app/actions/app-data'; wixAppClient.shippingRates.provideHandlers({ async getShippingRates({ request, metadata }) { const appData = await getShippingAppData({ instanceId: metadata.instanceId! }); const currency = metadata.currency; return { shippingRates: appData.shippingMethods.map(({ code, title, costs }) => ({ code, title, cost: { price: costs.first, currency, }, })), }; }, }); export async function POST(request: Request) { return wixAppClient.servicePlugins.processRequest(request); } ``` Let’s walk through this code and understand what it does: **Lines 1-2:** We import two objects: * The Wix Client that we set up in the `src/app/utils/wix-sdk.app` folder. This enables our app to communicate with the Wix platform. * The getter method we defined in `src/app/actions/app-data.ts`, which returns our default shipping data object when requested. **Line 4:** We call the `provideHandlers()` function to define handler functions that are called whenever a service plugin request is received. **Line 5:** We define the `getShippingRates()` handler function that is triggered whenever a request is received by the endpoint. All our logic goes in this function. **Line 6**: We call our getter method to return our default shipping data object. **Line 7:** We get the currency from the metadata sent with the site’s and set our currency type to match it. This ensures that the price we return is in the site's currency. **Lines 9-18:** The `getShippingRates()` handler function returns a valid response with our shipping rate data. In the function, we map our data to the fields in the [return object](https://dev.wix.com/docs/sdk/backend-modules/ecom/service-plugins/shipping-rates/get-shipping-rates.md) that Wix expects our function to return. Note that in line 15, we pass the site’s currency type to our object to ensure this shipping rate is returned in the correct currency. **Line 22:** We create a POST function that is called whenever a POST request is received by the endpoint. **Line 23:** This function does a lot of work! It parses the request our app receives from the site, validates its JWT signature, calls the handler function we defined above and passes it the request and associated metadata. Then, it returns the response as a JSON to the user's site, so the site will display the shipping rates to the customer. To learn more about what’s going on in the background, read about [verifying requests received from Wix](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/verify-requests-received-from-wix.md) and see this [sample flow](https://dev.wix.com/docs/rest/business-solutions/e-commerce/service-plugins/shipping-rates-integration-service-plugin/sample-flow.md). This code takes care of the service plugin handling part of our app, and now we can move on to creating the dashboard. ### Develop the dashboard page The dashboard page lets a site admin interact with your app. The page we build in this app will allow users to customize their shipping rates directly from their site dashboard. The code for the dashboard is spread over a number of files. We won’t review every line of code in detail, but we will show you what files you need to add and what code they should contain. We’ll start by adding several supporting files that we need to import into the main UI code. Then we’ll write the dashboard code itself. #### Before you begin Install the following packages if you don’t have them already: * `@tanstack/react-query` * `@wix/dashboard` * `@wix/design-system` #### Create a Wix Client with Dashboard SDK Auth Previously we [created a Wix Client](#create-a-wix-client-with-appstrategy) in `wix-sdk.app.ts` that uses WixAppOAuth to authenticate as an app instance. However, to perform actions on the dashboard, our app also needs to authenticate on behalf of the user. Therefore, we use the [Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) to create an additional client. 1. Navigate to the `src/app/utils` folder, and add a file called `wix-sdk.client-only.ts`. In this file, add the following code: ```javascript 'use client'; import { dashboard, SDK } from '@wix/dashboard'; import { useMemo } from 'react'; import { createClient } from '@wix/sdk/client'; function inIframe() { try { return window.self !== window.top; } catch (e) { return true; } } export const useSDK = () => { const sdk = useMemo( () => typeof window === 'undefined' || !inIframe() ? { dashboard: {} as SDK, } : createClient({ host: dashboard.host(), auth: dashboard.auth(), modules: { dashboard, }, }), [typeof window], ); return sdk; }; ``` This code contains two functions: * A utility function `inIframe()` that checks if the current window is displayed in an iFrame. This is necessary because the dashboard SDK won’t be initialized outside of an iFrame. This would make the SDK methods unusable. * A `useSDK()` function that creates a [dashboard SDK client](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md#setup) if it determines the window is embedded in an iFrame. Like the first client we created, `useSDK()` calls the Wix SDK’s [`createClient()`](https://dev.wix.com/docs/sdk/core-modules/sdk/wix-client.md#createclient) function. Now, however, we pass in `dashboard.host()` and `dashboard.auth()` to set up the authentication on behalf of the user. Our app now has the proper authorizations to work with the dashboard through the new Wix Client. #### Supporting files In addition to the Wix Client, we also need to add a few more supporting files. The code in these files provides necessary wrappers and enums to render our dashboard UI. 1. In `src/app/utils` add the following files: * `client-providers.tsx` * `navigation.const.ts` 2. In `client-providers.tsx`, add the following code: ```javascript 'use client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import type { PropsWithChildren } from 'react'; import { WixDesignSystemProvider } from '@wix/design-system'; export const AppProviders = ({ children }: PropsWithChildren) => { const queryClient = new QueryClient(); return ( {children} ); }; ``` The purpose of this code is to create a wrapper for the dashboard React component and define global variables. 3. In `navigation.const.ts`, add: ```javascript export enum WixPageId { SHIPPING_INFO = 'stores.shipping', MANAGE_APPS = 'ad471122-7305-4007-9210-2a764d2e5e57', MANAGE_ORDERS = '8107f05f-d646-4c81-be90-adf28d321398', } ``` This enum provides the page IDs that components of your app can navigate to. For example, on our dashboard page we will include breadcrumbs that allow the user to navigate back to the [**Manage Apps**](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fmanage-installed-apps) page of their site dashboard. ![Breadcrumbs example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d12a97db12f15a96e17be9fb7824ae40.png) For the breadcrumbs to work, you have to pass the relevant page IDs, as you will see in the dashboard code. We store the page IDs here so we can easily access them from anywhere in the code. 4. Now navigate to `src/app/client-hooks`. In the `client-hooks` folder, add a file called `app-data.ts`. Add the following code to the file: ```javascript 'use client'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getShippingAppData, setShippingAppData } from '@/app/actions/app-data'; const queryKey = ['shipping-app-data']; export const useShippingAppData = () => { return useQuery({ queryKey, queryFn: async () => { return getShippingAppData(); }, }); }; export const useSetShippingAppData = () => { const queryClient = useQueryClient(); const { mutateAsync } = useMutation({ mutationFn: async (newData: any) => { return setShippingAppData(newData); }, onSuccess: (data) => { void queryClient.invalidateQueries({ queryKey }); }, }); return mutateAsync; }; ``` The purpose of the code in this file is to wrap the setter and getter methods we created earlier in React Query, so that we can use them with the UI. Let’s go through this file in more detail to understand what the code is doing: **Line 1**: We indicate that this file is intended for client-side code with the `'use client'` [directive](https://react.dev/reference/react/use-client). **Line 2-3**: We import necessary functions from the `@tanstack/react-query` library. We also import the setter and getter methods that we created earlier. **Line 5**: We define a unique query key for fetching and validating the data. **Line 7-14**: We wrap our getter method, `getShippingAppData()`, in React Query. Remember that in this tutorial, `getShippingAppData()` just returns our default object. In a fully functioning app, it should fetch the data from an external database. **Line 16-27**: This function wraps our setter method in React Query, so that any changes to the data from the UI are stored in the database. Now that we’ve added the supporting files, we can export them to the dashboard code. #### The dashboard code The dashboard is broken up into a few different components, as shown below. ![Shipping Rate app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d9b3d61fdd9f56eba6106f1e1f5552fa.png) For this tutorial, we’ll only add the delivery method and header sections. We’ll explain how to add the activation and order summary sections so you can do it on your own. You can find the original template code for all these sections [here](https://github.com/wix/app-template-custom-shipping-rates/tree/main/src/app/dashboard/parts). 1. Navigate to `src/app/dashboard/parts` and add the following three files: * `ShippingDeliveryMethodForm.tsx` * `ShippingRatesPage.tsx` * `ShippingRatesPageContent.tsx` 2. Let’s start by adding the code to `ShippingDeliveryMethodForm.tsx`. This sets up the two delivery method sections (one is collapsed) shown in the image above. Start by adding the import statements to the file: ```javascript import { useState } from 'react'; import { Box, Card, Cell, Collapse, Dropdown, FormField, Input, Layout, Text, TextButton } from '@wix/design-system'; import { ChevronDown, ChevronUp } from '@wix/wix-ui-icons-common'; import { ShippingCosts, ShippingMethodType, ShippingUnitOfMeasure } from '@/app/types/app-data.model'; ``` As you can see, we import the types we created for handling shipping data in line 4. These types will make it easier to set up our dashboard page. In addition, in line 2, we import a number of elements from the [Wix Design System](https://www.wix-pages.com/wix-design-system/). We use these to construct the delivery method UI. The rest of the file exports a React functional component called `ShippingDeliveryMethodForm` that controls the UI of the delivery method sections. Add the function to your file: ```javascript export function ShippingDeliveryMethodForm({ title, unitOfMeasure, shippingCosts, onUnitOfMeasureSelected, onShippingCostsChanged, expandByDefault = false, }: { title: string; unitOfMeasure: ShippingUnitOfMeasure; shippingCosts: ShippingCosts; onUnitOfMeasureSelected: (type: ShippingUnitOfMeasure) => void; onShippingCostsChanged: (shippingCosts: ShippingCosts) => void; expandByDefault?: boolean; }) { const uomName = unitOfMeasure === ShippingUnitOfMeasure.NUM_OF_ITEMS ? 'item' : unitOfMeasure === ShippingUnitOfMeasure.WEIGHT_IN_LB ? 'lb' : 'kg'; const [isOpen, setIsOpen] = useState(expandByDefault); return ( setIsOpen(!isOpen)}>{isOpen ? : }} /> sameOptionWasPicked ? null : onUnitOfMeasureSelected(option.id as ShippingUnitOfMeasure) } options={[ { id: ShippingUnitOfMeasure.NUM_OF_ITEMS, value: 'Number of items' }, { id: ShippingUnitOfMeasure.WEIGHT_IN_KG, value: 'Weight in kg' }, { id: ShippingUnitOfMeasure.WEIGHT_IN_LB, value: 'Weight in lb' }, ]} placeholder='Select parameter' /> Set conditions: $} placeholder='Select totalPrice' type='number' value={shippingCosts.first} onChange={(e) => { onShippingCostsChanged({ ...shippingCosts, first: Number(e.currentTarget.value) }); }} /> $} placeholder='Select totalPrice' type='number' value={shippingCosts.second} onChange={(e) => { onShippingCostsChanged({ ...shippingCosts, second: Number(e.currentTarget.value) }); }} /> $} suffix={per {uomName}} value={shippingCosts.thirdAndUp} onChange={(e) => { onShippingCostsChanged({ ...shippingCosts, thirdAndUp: Number(e.currentTarget.value) }); }} placeholder='Select totalPrice' type='number' /> ); } ``` Let’s review the important parts of the code: **Lines 6-21:** We pass destructured `title`, `ShippingUnitOfMeasure` and `ShippingCosts` props to `ShippingDeliveryMethodForm`. We annotate each prop with Typescript to define its expected type. We also pass two callback functions, `onUnitOfMeasureSelected` and `onShippingCostsChanged` which perform actions in the UI when the measurement unit or costs are changed. We’ll define these callbacks in `ShippingRatesPageContent.tsx`. **Line 30:** The function returns a [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card) component containing header and content, as shown here: ![Standard delivery section](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb6768cb98a3fbcd5c2228e95ad7cb36.png) **Lines 38-54:** The function sets up a [Box](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--box) component that contains a dropdown. This lets users select the unit of measurement they wish to use. **Lines 54-98**: The function sets up another box that contains three cells. Each cell corresponds to the per-unit cost fields. For example, lines 57-69 set up the first cost field. 3. Now we’ll add the code to `ShippingRatesPageContent.tsx`. The code in this file drives the main logic of the UI, so we will go through it in more detail. Let’s start, once again, by adding the import statements: ```javascript 'use client'; import { Box, Breadcrumbs, Button, Cell, Layout, Loader, Page } from '@wix/design-system'; import { useSDK } from '@/app/utils/wix-sdk.client'; import { useCallback, useEffect, useState } from 'react'; import { ShippingDeliveryMethodForm } from '@/app/dashboard/parts/ShippingDeliveryMethodForm'; import { WixPageId } from '@/app/utils/navigation.const'; import { useSetShippingAppData, useShippingAppData } from '@/app/client-hooks/app-data'; import { ShippingAppData, ShippingCosts, ShippingUnitOfMeasure } from '@/app/types/app-data.model'; ``` **Line 1:** We mark the file as containing client-only code. **Line 2:** We import the elements we need from the [Wix Design System](https://www.wix-pages.com/wix-design-system/?path=/story/getting-started--about). **Line 3:** We import the `useSDK()` we created in `src/app/utils/wix-sdk.client`. This function allows our code to communicate with the dashboard SDK client. **Line 4:** We import several necessary React methods. We will use these shortly to define the logic of our code. **Line 5:** We import the `ShippingDeliveryMethodForm` function that we created in the previous step. We will integrate this into the overall dashboard page UI. **Line 6:** We import the `WixPageId` enum. Recall that we need the codes in this enum to navigate from our page to other pages in the dashboard. **Line 7**: We import the wrapped setter and getter methods. We’ll use these to respond to user actions in the UI. **Line 8:** We import our shipping data types. 4. Next, we export a React functional component called ShippingRatesPageContent. ```javascript export const ShippingRatesPageContent = ({}: {}) => { // We'll add code here } ``` Currently, this function doesn’t accept props, but you can add them later if required for your app. 5. Inside the `ShippingRatesPageContent()` function, first add this code: ```javascript const { dashboard: { showToast, navigate }, } = useSDK(); ``` We destructure the `sdk` object returned from `useSDK()` and extract two functions we need, [showToast()](https://dev.wix.com/docs/sdk/host-modules/dashboard/show-toast.md) and [navigate()](https://dev.wix.com/docs/sdk/host-modules/dashboard/navigate.md). 6. Next, define a variable to contain our setter wrapper function: ```javascript const persistShippingAppData = useSetShippingAppData(); ``` 7. In the next line, add this code: ```javascript const { data: persistedShippingAppData, isLoading: isLoadingAppData } = useShippingAppData(); ``` At some point, we’ll need to fetch the shipping data we’ve stored to display on the dashboard. We’ll store the fetched data in `persistedShippingAppData`. In this tutorial, we are simply returning the default object that’s hard-coded into our app, so `isLoadingAppData` is not that relevant. But you’ll need it later when you implement logic to fetch the data from a database. 8. Next, add the following code: ```javascript const [currentShippingAppData, setCurrentShippingAppData] = useState( persistedShippingAppData, ); useEffect(() => { setCurrentShippingAppData(persistedShippingAppData); }, [persistedShippingAppData]); const [loading, setLoading] = useState(false); ``` We manage the state of the data displayed on the dashboard with the `useState()` React hook. We also manage the loading state. We use the loading state later in the UI to display a [loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader) element. We keep the displayed shipping data up-to-date with the React hook `useEffect()`. If a user makes changes to the shipping data stored in the database, the new data is displayed on the dashboard when the component reloads. 9. Now add the callback function for saving updated shipping data: ```javascript const onSave = useCallback(() => { setLoading(true); persistShippingAppData(currentShippingAppData!) .then(() => { showToast({ message: 'Shipping rates saved successfully.', type: 'success', }); }) .catch(() => { showToast({ message: 'Failed to save shipping rates.', type: 'error', }); }) .finally(() => setLoading(false)); }, [persistShippingAppData, currentShippingAppData, showToast]); ``` The `onSave()` function passes the input shipping data to the setter method, which updates the data (in our case, in the default object; normally, in a database). While updating, it displays the loader, which is controlled by `setLoading()`. When the update completes, it uses the [showToast()](https://dev.wix.com/docs/sdk/host-modules/dashboard/show-toast.md) SDK method to indicate success or failure before clearing the loader. 10. Beneath the first callback, add another two that look like this: ```javascript const setUomForMethod = useCallback( (code: string) => (type: ShippingUnitOfMeasure) => { setCurrentShippingAppData({ ...currentShippingAppData, shippingMethods: currentShippingAppData!.shippingMethods.map((m) => m.code === code ? { ...m, unitOfMeasure: type } : m, ), }); }, [currentShippingAppData], ); const setCostsForMethod = useCallback( (code: string) => (costs: ShippingCosts) => { setCurrentShippingAppData({ ...currentShippingAppData, shippingMethods: currentShippingAppData!.shippingMethods.map((m) => (m.code === code ? { ...m, costs } : m) ), }); }, [currentShippingAppData], ); ``` These two callbacks fire when a user updates the unit of measurement or the shipping costs. They update the displayed shipping data to match the user’s selection. In `ShippingDeliveryMethodForm()`, we defined two props to receive callback functions. We’ll pass the two callback functions we define here as those arguments, as you’ll see shortly. 11. Define the UI and behavior for the button bar, using the [Box](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--box) and [Button](https://www.wix-pages.com/wix-design-system/?path=/story/components-actions--button) components from the Wix Design System: ```javascript const ButtonsBar = useCallback( () => ( ), [loading, onSave, persistedShippingAppData], ); ``` 12. Last, return the UI component. We’ll walk through the code so you can see how everything comes together. Start by adding the return statement, and inside add a [Page](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page) component with a `Page.Header` and a `Page.Content` element. Inside the `Page.Content`, add a cell and wrap it in a [Layout](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--layout) element. Set the Cell component’s `span` prop to 8. It should look like this: ```javascript return ( ) ``` 13. Add code to the header element: ```javascript return ( } breadcrumbs={ navigate(id as string)} /> } title='Shipping Rate App' subtitle='Customize shipping fees based on item quantity, weight, and delivery speed, ensuring a flexible and cost-effective solution for your business.' /> ``` The page header contains the buttons bar that we set up previously, the page title and subtitle, and breadcrumbs. Notice in the [Breadcrumbs](https://www.wix-pages.com/wix-design-system/?path=/story/components-navigation--breadcrumbs) element, we pass the [page ID](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/utils/navigation.const.ts) for the **Manage Apps** dashboard page. This lets users navigate to **Manage Apps** from your app’s dashboard page. 14. Now add the page content. We’re going to add a ternary operator that determines the page content based on the value of `isLoadingAppData`: ```javascript {isLoadingAppData ? ( ) : ( {currentShippingAppData?.shippingMethods.map((method, index) => ( ))} )} ``` If the app is still loading the shipping data from the backend, we display a [Loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader). Once the shipping data is loaded, we update `isLoadingAppData` to `false` to display the data. The second part of the ternary expression iterates over an array of shipping method elements fetched from the database. For each element, it populates the props of a `ShippingDeliveryMethodForm` component. The component then uses these props to create the UI of each delivery method. The dashboard page of your app now closely resembles that of the template. Here is the full code for `ShippingRatesPageContent.tsx`: ```javascript 'use client'; import { Box, Breadcrumbs, Button, Cell, Layout, Loader, Page } from '@wix/design-system'; import { useSDK } from '@/app/utils/wix-sdk.client'; import { useCallback, useEffect, useState } from 'react'; import { ShippingDeliveryMethodForm } from '@/app/dashboard/parts/ShippingDeliveryMethodForm'; import { WixPageId } from '@/app/utils/navigation.const'; import { useSetShippingAppData, useShippingAppData } from '@/app/client-hooks/app-data'; import { ShippingAppData, ShippingCosts, ShippingUnitOfMeasure } from '@/app/types/app-data.model'; export const ShippingRatesPageContent = ({}: {}) => { const { dashboard: { showToast, navigate }, } = useSDK(); const persistShippingAppData = useSetShippingAppData(); const { data: persistedShippingAppData, isLoading: isLoadingAppData } = useShippingAppData(); const [currentShippingAppData, setCurrentShippingAppData] = useState( persistedShippingAppData, ); useEffect(() => { setCurrentShippingAppData(persistedShippingAppData); }, [persistedShippingAppData]); const [loading, setLoading] = useState(false); const onSave = useCallback(() => { setLoading(true); persistShippingAppData(currentShippingAppData!) .then(() => { showToast({ message: 'Shipping rates saved successfully.', type: 'success', }); }) .catch(() => { showToast({ message: 'Failed to save shipping rates.', type: 'error', }); }) .finally(() => setLoading(false)); }, [persistShippingAppData, currentShippingAppData, showToast]); const setUomForMethod = useCallback( (code: string) => (type: ShippingUnitOfMeasure) => { setCurrentShippingAppData({ ...currentShippingAppData, shippingMethods: currentShippingAppData!.shippingMethods.map((m) => m.code === code ? { ...m, unitOfMeasure: type } : m, ), }); }, [currentShippingAppData], ); const setCostsForMethod = useCallback( (code: string) => (costs: ShippingCosts) => { setCurrentShippingAppData({ ...currentShippingAppData, shippingMethods: currentShippingAppData!.shippingMethods.map((m) => (m.code === code ? { ...m, costs } : m)), }); }, [currentShippingAppData], ); const ButtonsBar = useCallback( () => ( ), [loading, onSave, persistedShippingAppData], ); return ( } breadcrumbs={ navigate(id as string)} /> } title='Shipping Rate App' subtitle='Customize shipping fees based on item quantity, weight, and delivery speed, ensuring a flexible and cost-effective solution for your business.' /> {isLoadingAppData ? ( ) : ( {currentShippingAppData?.shippingMethods.map((method, index) => ( ))} {/* */} )} {/* */} {/* */} {/* */} ); }; ``` Notice in the complete code, two sections have been commented out. These sections define two more components that we've excluded for this tutorial: * The activation details card. * The order summary. These components aren’t necessary for the app to work, but the full app template includes them. If you want to add them to your app, you can use the code in the app template repo as follows: ##### Add activation details The activation details card provides instructions to the site admin on how to activate your app on their site. You can find the code [in the app template repo](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/dashboard/parts/ActivationDetailsCard.tsx). Notice that you need to use the Wix page ID to direct the user to the correct page. ##### Add order summary The order summary section provides the site admin with a list of recent orders made on their site. For this section to work, you’ll need to create a third Wix client that initializes the Wix eCommerce Orders API, and implement code to fetch the site’s last orders. There are several steps to do this: 1. Install the Wix JavaScript SDK’s `orders` module and import it into your client code file as shown [here](https://dev.wix.com/docs/sdk/backend-modules/ecom/orders/setup.md). 2. In the same file, add the [code](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/utils/wix-sdk.ts) for the client: ```javascript export const createSdk = (accessToken: string) => createClient({ auth: { getAuthHeaders: async () => ({ headers: { Authorization: accessToken, }, }), }, modules: { orders, }, }); ``` Pass the Orders module into `createClient()`, so that the client will initialize the module for use. 3. Now you can export the client to other files and call functions belonging to the `orders` module. For example, you can use the [`searchOrders()`](https://dev.wix.com/docs/sdk/backend-modules/ecom/orders/search-orders.md) function to get a list of orders that have been made on your site: ```javascript sdk.orders.searchOrders({ search: { cursorPaging: { limit: 3, }, }, }) ``` See the [full code](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/actions/orders.ts). Once you implement code to retrieve recent orders, you need to [wrap it in React Query](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/client-hooks/orders.ts). Then you can use the wrapped code to populate the UI with the order data. In the [app template code](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/dashboard/parts/ShippingMethodSummary.tsx), we use a [sticky area](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page#Sticky_areas) to display the order summary. #### Render the dashboard page At this point, we’ve completed the bulk of the code needed to define our dashboard. We need to add code to two more files so our dashboard page will render on a site. 1. In `ShippingRatesPage.tsx`, add the following code: ```javascript 'use client'; import '@wix/design-system/styles.global.css'; import { AppProviders } from '@/app/utils/client-providers'; import { ShippingRatesPageContent } from '@/app/dashboard/parts/ShippingRatesPageContent'; export const ShippingRatesPage = ({}: {}) => { return ( ); }; ``` We wrap `ShippingRatesPageContent` in the `AppProviders` component we created in our supporting code. This allows the UI code we wrote to be accessed by the entire app. 2. In `page.tsx`, copy and paste this code: ```javascript import { ShippingRatesPage } from '@/app/dashboard/parts/ShippingRatesPage'; export default async function App() { return ; } ``` This will render the dashboard UI on the site. You now have a custom shipping rates Wix app with basic functionality and a dashboard page. You can continue expanding on it from here, or you can deploy and test it in the next steps. ## Step 5 | Deploy your app To deploy our app, we have to do the following: 1. Set it up on a platform like [Netlify](https://www.netlify.com/) or [Vercel](https://vercel.com/). 2. Complete the setup in the app dashboard. ### Set up your app on a deployment platform 1. Connect the Git repository containing your app code to the deployment platform you choose. 2. Add the three variables from your `.env.local` file to your site’s environment variables on the deployment platform. 3. Deploy your app and copy its deployment domain so you have it for the next steps. ### Finish configuring the app in your app's dashboard Now we’ll return to your app's dashboard to connect our deployed app. We need to add and configure the following extensions to our app so it will work on a site: 1. **Shipping Rates extension**: Enables our app to communicate with the Get Shipping Rates endpoint we implemented. 2. **Dashboard Page extension**: Integrate the dashboard UI we created in our code. #### Add a Shipping Rates Extension 1. From the app's dashboard, click **Extensions** > **Create Extension**. 2. In the modal that pops up, search for “shipping rates” in the search bar. You should see an extension called **Ecom Shipping Rates**. ![Choose shipping rates extension](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a04674976e634ba681d496cc2aac00ad.png) 3. Click on the **Create** button to add a new Ecom Shipping Rates extension. This will bring up a JSON editor that looks like this: ![Shipping Rates JSON editor](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a4e37ac0b8075e95f63c991fcba86302.png) 4. You’re required to define `deploymentUri` and `name` in your JSON. You can give your app any name. The deployment URI should consist of your app’s deployment domain with the path `/api/shipping-rates` appended to it: ```bash https:///api/shipping-rates ``` Below is an example of a JSON with the `name` and `deploymentUri` keys defined. ```json { "deploymentUri": "https://my-example-shipping-rates-app.vercel.app/api/shipping-rates", "name": "My Example Shipping Rates app" } ``` When your JSON is complete, save it and exit the JSON editor to return to the main **Extensions** page. #### Add a Dashboard Page extension 1. Click **Create Extension** and create a new **Dashboard Page**. ![Select Dashboard Page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/34d2a49af022e9ba9921ef8215a9df92.png) > **Note:** > Select the dashboard page option that is **not** built on Wix Blocks. You should now see something like this: ![Configure dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7ec9c7611d6f4411ca6d67c6f7fa2590.png) 2. Fill out the following fields: * **Name**: This is the name of the page that you’ll see in your app's dashboard. * **iFrame URL**: This is the domain from your deployment platform. Our dashboard code is in the `dashboard` directory of our app, so we append `/dashboard` to the domain. For example, if you deployed on Vercel and your domain is `my-tutorial-app.vercel.app`, your iFrame URL should be: ```bash https://my-tutorial-app.vercel.app/dashboard ``` * **Relative route**: This will appear as your relative page URL on the user’s dashboard. For example: `shipping-rates-dashboard`. * **Page name**: This is the name of your page that will appear on the side menu on the user’s dashboard. For example: `Custom Shipping Rates`. 3. Click **Save** to exit. Your app is now set up and ready to be installed on a site. ## Step 6 | Test your app on a site Now that your app has basic functionality, you can install it on a free [development site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app-on-a-premium-site.md) to test it. We’ll walk through how to set up a test site so we can use our app with it, then show you how to test the installed app itself. ### Install the app on a development site 1. In the top right corner of your [app's dashboard](https://manage.wix.com/account/custom-apps), click **Test App** and select **Test on dev site**. 1. Select an existing development site or click **+ Create Dev Site** to create a new site. Select the editor and the Wix Business Solution you want to use and click **Create Dev Site**. 1. Click **Test App**. Wix installs your app and opens the site in a new tab. You can set which site page opens in your [app settings](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fapp-settings). If you don't set a page, the site editor opens by default. ### Try out the app Let’s start by looking at the dashboard we created: 1. Go to your development site’s [dashboard](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fhome). 2. In the left sidebar, click **Apps**. In the submenu that opens, click the name you chose for your app, such as Custom Shipping Rates. 3. You can test out the functionality of the dashboard by changing the parameter and the costs in each cell. Now let’s see the app’s logic in action: 1. Once the app is installed, go to your site dashboard and navigate to **Settings** > **[Shipping, Delivery & Fulfillment](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fstore/shipping-and-tax/list)**. 2. Click on the **Create Region** button. A new page will open. In the **Create Region** section, choose a region name, click **Add Destination** and select at least one country from the list. 3. Select delivery method and rate. On the next page, select the delivery method that you want to offer customers and choose the rate for that method. Click **Add**. 4. Enable the custom app. A new destination item is added to the page. On this destination, select **Manage your Apps** and you'll see your app. Click the toggle to turn it on. Click **Save**. 5. Go back to the editor and publish your site. View your published site and navigate to the product page. 6. Select any product and add it to the cart, then view the cart. Under **Delivery**, select the country you chose to add shipping rates to. ![Shipping rates live example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2170a5ff86aab2e3d06edaee0a56b6e3.png) You should see the delivery price change to 5 units of your site’s currency (for example, €5). This comes directly from the default shipping data object we created in `src/app/actions/app-data.ts`. We return the price of the first item as the shipping cost. ### Run the app locally Since you have already set up your local environment variables in the .env.local file, you can also run your app locally. To do so, do the following: 1. In the root folder of your project repo, install dependencies: ```bash npm install ``` 2. You need to manually change your app URL in your app's dashboard. Replace your current base URL with `https://localhost:3000/` in the following places: 1. OAuth page: App URL and Redirect URL for the [app OAuth flow](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-oauth.md). 2. Webhooks page: App Removed and App Installed [webhooks](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/events-and-webhooks/about-webhooks.md). 3. Reinstall the app on your test site. 4. To start the development server, run the command: ```bash npm run dev ``` You can now install the app on a Wix site with server code running locally. > **Note:** > Remember to revert to the deployment URLs in your app's dashboard when you are ready to switch your app to production server deployment. You'll need to reinstall the app again for the change to take effect. ## What’s next Now that your app is working and has some basic functionality, you can continue building on it. Here are some places to help you get going: * Check out the full [custom shipping rates app template code](https://github.com/wix/app-template-custom-shipping-rates) for ideas. * View our [Build Wix Apps documentation](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-apps.md) to answer any questions. ## Table of Files This table describes the purpose of each file in the original code. > **Note:** > The table does not include config files such as `.gitignore` or `package.json`, or other files generated automatically by `create-next-app`. | File path | Description | | --------- | ----------- | | `src/app/actions/app-data.ts` | Contains several functions for fetching and updating shipping rates data in an external database.| | `src/app/actions/app-instance.ts` | Parses a Wix decoded instance to extract the instance id.| | `src/app/actions/orders.ts` | Retrieves recent orders made on the site to provide the [order summary](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/dashboard/parts/ShippingMethodSummary.tsx) on the dashboard page.| | `src/app/api/oauth/v1/authorize/route.ts` | Sets up the request handler for the app url Oauth implementation. The user is required to add their own logic, as described in [Custom Authentication (Legacy)](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/custom-authentication-legacy.md).| | `src/app/api/oauth/v1/signup/route.ts` | Sets up the request handler for the app url Oauth implementation. The user is required to add their own logic, as described in [Custom Authentication (Legacy)](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/custom-authentication-legacy.md).| | `src/app/api/shipping-rates/v1/getRates/route.ts` | Provides the main logic for receiving and responding to service plugin requests from the user’s site.| | `src/app/api/webhooks/v1/install/route.ts` | An empty webhook implementation for app installation. The user can add their own logic.| | `src/app/api/webhooks/v1/uninstall/route.ts` | An empty webhook implementation for uninstalling the app. The user can add their own logic.| | `src/app/client-hooks/access-token.ts` | A React hook which uses the Wix Dashboard SDK to fetch the current user’s access token.| | `src/app/client-hooks/app-data.ts` | React hooks to define the shipping data getter and setter methods.| | `src/app/client-hooks/orders.ts` | Wraps the code to fetch the latest orders in React Query so it can be used in the UI.| | `src/app/dashboard/parts/ActivationDetailsCard.tsx` | Builds the UI for the activation instructions in the dashboard.| | `src/app/dashboard/parts/ShippingDeliveryMethodForm.tsx` | Builds the UI for the main part of the dashboard where the user can adjust shipping rates.| | `src/app/dashboard/parts/ShippingMethodSummary.tsx`| Builds the UI for the order summary in the dashboard.| | `src/app/dashboard/parts/ShippingRatesPage.tsx` | Wraps the dashboard page content in the [AppProviders](https://github.com/wix/app-template-custom-shipping-rates/blob/main/src/app/utils/client-providers.tsx) wrapper for React.| | `src/app/dashboard/parts/ShippingRatesPageContent.tsx` | Builds the overarching UI for the dashboard page and pulls in the main components: the activation details, order summary, and shipping rates forms.| | `src/app/dashboard/page.tsx` | Renders the dashboard components on the site.| | `src/app/types/app-data.model.ts` | Creates several types for handling the shipping rates data.| | `src/app/types/order.ts` | Creates an order summary type used when [fetching previous orders](https://github.com/wix/app-template-custom-shipping-rates/blob/c96a6015c03039534bb491242c014147788e1f10/src/app/actions/orders.ts#L8) for display on the dashboard page.| | `src/app/utils/client-providers.tsx` | Creates the AppProviders wrapper to wrap the dashboard page component.| | `src/app/utils/jwt-verify.ts` | Parses the Wix instance in order to pull the app instance ID.| | `src/app/utils/navigation.const.ts` | Adds page ID information to the app so it can interact with the dashboard page.| | `src/app/utils/shipping-calculator.ts` | Implements a function to calculate shipping rates prices from the ShippingAppData object.| | `src/app/utils/wix-sdk.app.ts` | Creates the Wix Client that authenticates as the app.| | `src/app/utils/wix-sdk.client-only.ts` | Creates the Wix client that authenticates with user permissions and identity. Later used in the dashboard.| | `src/app/utils/wix-sdk.ts` | Creates a Wix SDK client that can be used in both server and client components.| --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create an Editor Add-on that Imports Icons from a Library ## Article: Tutorial | Create an Editor Add-on that Imports Icons from a Library ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-an-editor-add-on-that-imports-icons-from-a-library.md ## Article Content: # Tutorial | Create an Editor Add-on that Imports Icons from a Library In this tutorial, we’ll show you how to create an [editor add-on](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/editor-extensions/about-editor-add-on-extensions.md) from scratch. This add-on lets users import icons from a predefined library directly into their site by uploading them to the site's media files and applying them to a selected element in the Wix Editor. ![add-on](https://wixmp-833713b177cebf373f611808.wixmp.com/images/afce5fd79d438e21c27a5094b8537748.png) We'll follow these steps to build the app: 1. Create a React project using Vite. 2. Run the project locally. 3. Set up your app in the Wix Custom Apps page. 4. Test the app on a site. While we’re using Vite as the build tool, you can choose any local development server that suits your needs. ## Before you begin Before getting started, make sure that: * You are signed into your [Wix account](https://manage.wix.com/account/custom-apps). * You have [Node.js](https://nodejs.org/en/) 20 or higher installed. * You have a Wix site that you can use for testing purposes. If you don’t have one yet, [create a new site](https://www.wix.com/website/templates/html/blank). ## Step 1 | Set up a React project Start by setting up a new React project with Vite. We’ll follow these steps: 1. Bootstrap the React project. 2. Install dependencies. 3. Configure Vite. 4. Create React application files. ### Bootstrap a React project with Vite 1. Run the following command in your terminal: ```bash npm create vite@latest my-add-on-app -- --template react-ts ``` 2. Navigate to your new project folder: ```bash cd my-add-on-app ``` ### Add dependencies in the `package.json` file and install them Next, we'll install the required dependencies for the app. These include: * **[`@wix/design-system`](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md)** – Ensures the UI follows Wix’s design guidelines, providing components like `Box`, `Text`, and `Button`. * **[`@wix/editor`](https://dev.wix.com/docs/sdk/host-modules/editor/introduction.md)** – Enables interaction with the Wix Editor, including selecting and modifying site elements. * **[`@wix/media`](https://dev.wix.com/docs/sdk/backend-modules/media/introduction.md)** – Handles uploading the selected icon to Wix Media storage for use on the site. * **[`@wix/sdk`](https://dev.wix.com/docs/sdk/core-modules/sdk/introduction.md)** – Provides API access to Wix services like `files` and `elements`, allowing the add-on to modify editor elements. * **`vite-plugin-mkcert`** – Enables HTTPS support for local development, which is required for secure communication with Wix. * **`vite-plugin-node-polyfills`** – Ensures compatibility with Node.js APIs within a Vite-based front-end environment. * **`@emotion/react` & `@emotion/styled`** – Required by Material UI for styling components dynamically. * **`@mui/icons-material` & `@mui/material`** – Provides the Material UI components and icons used for the icon selection interface. 1. Open `package.json` and add the following dependencies under `"dependencies"`: ```json "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.3", "@mui/material": "^6.4.3", "@wix/design-system": "^1.158.0", "@wix/editor": "^1.348.0", "@wix/media": "^1.0.145", "@wix/sdk": "^1.15.10" ``` 2. Under `"devDependencies"`, add: ```json "vite-plugin-mkcert": "^1.17.6", "vite-plugin-node-polyfills": "^0.23.0" ``` 2. Install the dependencies: ```bash npm install --legacy-peer-deps ``` ### Configure Vite Update the `vite.config.ts` file in your project root with the following code to enable HTTPS for local development and add Node.js polyfills: ```typescript import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import mkcert from "vite-plugin-mkcert"; import { nodePolyfills } from "vite-plugin-node-polyfills"; export default defineConfig({ plugins: [ react(), mkcert(), // Allows serving the app over HTTPS locally nodePolyfills({ globals: { global: true, }, }), ], }); ``` ### Create application files We'll now create the core application files needed for the project: * `main.tsx`: This is the entry point of your application. Update it to import Wix Design System's style sheet. * `App.tsx`: This file contains the main structure of the app, showing a button for adding icons, a loader for upload states, and a message for the user. * `Icons.tsx`: Displays the available icons in the library using Material UI's `IconButton`. Icons can be selected, and a state is updated for the chosen icon. * `useIconsManager.tsx`: Manages icon selection, uploading, and handling interactions with the Wix media service. This includes logic for converting the selected icon into an SVG and uploading it to site's media files, and applying it to the selected element in the Wix editor. Ensure your `src` directory includes `App.tsx` and `main.tsx` files and update their content as follows:
src/main.tsx ```tsx import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App.tsx' import '@wix/design-system/styles.global.css'; createRoot(document.getElementById('root')!).render( , ) ```
src/App.tsx ```tsx import { Box, Button, Divider, Loader, Text, WixDesignSystemProvider, } from '@wix/design-system'; import { Icons } from './Icons'; import { useIconsManager } from './useIconsManager'; const App = () => { const { toggleIconSelection, selectedIcon, isUploading, uploadIcon } = useIconsManager(); return ( Select an image on the canvas and choose the icon you want to add. ); }; export default App; ```
In your `src` directory create the following files:
src/Icons.tsx ```tsx import { FC } from 'react'; import { IconButton } from '@mui/material'; // Import all required icons import AddIcon from '@mui/icons-material/Add'; import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; import HomeIcon from '@mui/icons-material/Home'; import SettingsIcon from '@mui/icons-material/Settings'; import SearchIcon from '@mui/icons-material/Search'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import AlarmIcon from '@mui/icons-material/Alarm'; import BuildIcon from '@mui/icons-material/Build'; import CloudIcon from '@mui/icons-material/Cloud'; import FavoriteIcon from '@mui/icons-material/Favorite'; import HelpIcon from '@mui/icons-material/Help'; import InfoIcon from '@mui/icons-material/Info'; import LanguageIcon from '@mui/icons-material/Language'; import LockIcon from '@mui/icons-material/Lock'; import MailIcon from '@mui/icons-material/Mail'; import MenuIcon from '@mui/icons-material/Menu'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import NotificationsIcon from '@mui/icons-material/Notifications'; import PauseIcon from '@mui/icons-material/Pause'; import PersonIcon from '@mui/icons-material/Person'; import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PowerIcon from '@mui/icons-material/Power'; import RefreshIcon from '@mui/icons-material/Refresh'; import SaveIcon from '@mui/icons-material/Save'; import SecurityIcon from '@mui/icons-material/Security'; import ShareIcon from '@mui/icons-material/Share'; import StarIcon from '@mui/icons-material/Star'; import { Box } from '@wix/design-system'; // Map the icons to their names const iconsMap: Record = { Add: AddIcon, Delete: DeleteIcon, Edit: EditIcon, Home: HomeIcon, Settings: SettingsIcon, Search: SearchIcon, AccountCircle: AccountCircleIcon, Alarm: AlarmIcon, Build: BuildIcon, Cloud: CloudIcon, Favorite: FavoriteIcon, Help: HelpIcon, Info: InfoIcon, Language: LanguageIcon, Lock: LockIcon, Mail: MailIcon, Menu: MenuIcon, MoreHoriz: MoreHorizIcon, Notifications: NotificationsIcon, Pause: PauseIcon, Person: PersonIcon, PlayArrow: PlayArrowIcon, Power: PowerIcon, Refresh: RefreshIcon, Save: SaveIcon, Security: SecurityIcon, Share: ShareIcon, Star: StarIcon, }; type IconsProps = { onSelect: (iconName: string) => void; selectedIcon: string | null; disabled: boolean; }; export const Icons: FC = ({ onSelect, selectedIcon, disabled }) => { return ( {Object.entries(iconsMap).map(([iconName, IconComponent]) => (
onSelect(iconName)} disabled={disabled} >
))}
); }; ```
src/useIconsManager.tsx ```tsx import { editor, elements } from '@wix/editor'; import { files } from '@wix/media'; import { createClient } from '@wix/sdk'; import { useState } from 'react'; const client = createClient({ modules: { files, elements }, host: editor.host(), auth: editor.auth(), }); const assertSelectedComponent = async (selectedIcon: string | null) => { const [selectedComponent] = await client.elements.getSelection(); if (selectedComponent.type !== 'image') { throw new Error('Selected component should be of type `image`'); } if (!selectedIcon) { throw new Error('Icon is not selected'); } const svgElement = document .getElementById(selectedIcon) ?.querySelector('svg'); if (!svgElement) { throw new Error(`Could not found an element with id ${selectedIcon}`); } return { svgElement, selectedComponent }; }; export const useIconsManager = () => { const [selectedIcon, setSelectedIcon] = useState(null); const [isUploading, setIsUploading] = useState(false); const uploadIcon = async () => { try { setIsUploading(true); setSelectedIcon(null); const { svgElement, selectedComponent } = await assertSelectedComponent( selectedIcon, ); const contentType = 'image/svg+xml'; const fileName = `${selectedIcon}.svg`; // get blob const svgString = new XMLSerializer().serializeToString(svgElement); const blob = new Blob([svgString], { type: contentType }); // Generate upload URL const { uploadUrl } = await client.files.generateFileUploadUrl( contentType, { filePath: 'images', fileName, }, ); // Define parameters const params = { filename: fileName }; const queryString = new URLSearchParams(params).toString(); // Send the request to upload the file const response = await fetch(`${uploadUrl}?${queryString}`, { method: 'PUT', headers: { 'Content-Type': contentType, }, body: blob, }); // parse the response of the image upload const { file } = await response.json(); // replace image on the page await selectedComponent.setProp('src', file.url); } catch (error) { console.log(error); } finally { setIsUploading(false); } }; const toggleIconSelection = (iconName: string) => { setSelectedIcon(selectedIcon === iconName ? null : iconName); }; return { toggleIconSelection, uploadIcon, isUploading, selectedIcon, }; }; ```
## Step 2 | Run your project Start the development server by running the following command in your terminal: ```bash npm run dev ``` Once the server starts, Vite will provide a local development URL (typically `https://localhost:5173`). Open your browser and navigate to this URL to preview your app. You can view and interact with the UI in your browser. However, the add-on functionality will not work outside the Wix Editor. In the next step, we'll set it up as a Wix app extension so we can test it properly within the Wix Editor. ## Step 3 | Set up your app in the Custom Apps page To make the add-on available for installation on sites, we'll create a Wix app and set up an add-on extension. 1. In the [Custom Apps page](https://manage.wix.com/account/custom-apps), go to **My Apps**, and then click **Create New App**. 2. Select **Build from scratch**. 3. In the left sidebar, select **Extensions**, and then click **Create Extension**. A panel opens, showing the available extension types. 4. Find the **Editor Add-on** extension and click **Create**. 5. Configure the add-on’s basic data: * **Add-on name**: Enter a name for your add-on, for example, `Get Icons`. * **Panel URL**: Enter the localhost URL provided by Vite in the previous step. * **Panel width**: Choose `Small` to match the size of most native editor panels. * **Panel height**: Set the height to `400 px`. 6. Under **Market listing**, fill in the **Teaser** field with a short description, such as `Import icons from the Material UI library`. 7. Click **Save**. 8. Add the required permissions to the app so it can access a site's media files: 1. In the left sidebar, select **Permissions**, and then click **Add Permissions**. 2. Find the **Manage Media Manager** permission scope, select it, and then click **Save**. Your add-on extension is now available for installation on a site. ## Step 4 | Test your add-on in the editor To test the add-on, we'll install it on a site: 1. In the top right corner of your [app's dashboard](https://manage.wix.com/account/custom-apps), click **Test App** and select **Test on dev site**. 1. Select an existing development site or click **+ Create Dev Site** to create a new site. Select the editor and the Wix Business Solution you want to use and click **Create Dev Site**. 1. Click **Test App**. Wix installs your app and opens the site in a new tab. You can set which site page opens in your [app settings](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fapp-settings). If you don't set a page, the site editor opens by default. 1. In the editor's **Tools** menu, select **Editor Add-ons**, and then select your add-on. Your add-on’s panel is displayed. 1. Add an image element to the page and select it. 1. Select one of the icons in the add-on panel and click **Add Icon**. The add-on should successfully upload the icon to the site's media files and apply it to the selected image element. ## What’s next Now that you've learned how to set up a self-managed editor add-on, you can start thinking about building your own app. Here are a few resources to get you started: * Learn more about [editor add-ons](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/editor-extensions/about-editor-add-on-extensions.md). * Learn more about [self-managed apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting-for-wix-apps.md). * Explore our [JavaScript SDK](https://dev.wix.com/docs/sdk.md). * Discover opportunities for your next app by exploring our [list of top user-requested features](https://dev.wix.com/docs/build-apps/get-started/get-an-idea.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Coffee Catalog with Blocks ## Article: Creating a Coffee Catalogue ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-coffee-catalog-with-blocks.md ## Article Content: # **Tutorial: Create a Coffee Catalog with Blocks**
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
Learn how to build a coffee catalog app using **Wix Blocks**. The app includes a widget that presents the products in a repeater, including a nested repeater for various rating options. It also allows users to configure it through dashboard pages. ![see app](https://wixmp-833713b177cebf373f611808.wixmp.com/images/29796ce21d7172da29cdd707c1582903.png) ## Before You Begin To help you get started quickly, we’ve provided a template of the app. Follow the next steps to get your own copy of the template. 1. Make sure you're logged in to your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). 1. Come up with a unique string for your app's namespace. If you do this tutorial more than once under the same Wix account, provide a different string each time. 1. Copy the following URL to your clipboard (but do not paste it in your browser yet). ```ts https://dev.wix.com/apps-templates/open-blocks-template?templateId=f0dbfb81-0793-4c24-a732-9c5b6dfe4273&namespace=&templateName=coffee-catalog ``` 1. Insert your unique string in the designated place, after the term `namespace=`. Make sure to delete the brackets. 1. Go to your browser and paste the link you created. Blocks will open with your own copy of the app. The app you just created includes the following segments for you to work with: - **Products widget** – You’ll complete its design and functionality. - **Taste Notes widget** – You'll add this as a nested widget inside the Products widget. - **Public utilities** – Ready-to-use functions for implementing your widget’s logic. - **Dashboard pages** - For users to edit, add and delete products. ## **Step 1 | Add a Nested Widget** First, complete the **Products widget** by adding the **Taste Notes widget** as a [nested widget](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-widgets/add-nested-widgets-in-blocks.md). 1. In the **App Interface** menu, select the **Products widget**. 2. Click **+ Add Elements** > **My Widgets**. 3. Click on the **Taste Notes widget** to add it inside the main widget.
See how it looks ![Add widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1d4d14c93b891f8b354db60f543cc361.png)
## **Step 2 | Adjust the nested widget’s design** When added, the **Taste Notes widget** appears in a random location inside the **Products widget**. Let’s position it properly. 1. Click on the **Taste Notes widget** to select it. 2. Drag it to the empty space in one of the repeater columns, just above the button. 3. Open the **Inspector**, then select the fourth row to place the widget there. 4. Click the **Stretch** ![stretch](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2023/10/04/20b8e657-f5de-41a1-802a-3425e8110a69/50ca7b0b-3348-4d1a-84ef-b78677e81686.png) icon to adjust its size. Now, the nested widget is neatly placed within the main widget.
See how it looks ![Design nested widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/6fc11bd602f63302a202faaf81234f8d.png)
## **Step 3 | Explore the Layers panel** Now is a good time to check the **Layers panel** to understand the structure of widgets and the repeaters in them: - The **main repeater** is the product catalog, named **productsRepeater**. - Inside it, there's a **nested list of taste notes**, named **notesRepeater**. - Inside the **notesRepeater**, there's another repeater displaying **intensity ratings**, named **rating**.
See how it looks ![Repeaters structure](https://wixmp-833713b177cebf373f611808.wixmp.com/images/39457987d863bcfbf38c292d0c52bd41.png)
## **Step 4 | Add and manage the Products collection** Let’s create [a collection](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md), which will store data for the coffee catalog. The collection includes the following fields: - **Origin Country** – Text - **Price** – Number - **Coffee Image** – Image URL - **Taste Notes** – Array The **Taste Notes** field contains an array of title-value pairs representing flavor intensity, for example: `["crisp:3","caramel:2","nutty:2"]`
See the Taste Notes design ![Taste Notes](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f25369d8ebdb86b2ccdd73aa469b8370.png)
### Download the data file and create the collection To save time, we’ve created a data file with five sample items for you. 1. [Download the file](https://8d8f933e-b7b3-441d-8a83-b8f6a97ecedf.filesusr.com/ugd/9580ec_a1e3b672f6bc432099db2bb072494478.csv?dn=Coffee.csv). 2. Click the **CMS** icon ![databases icon](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2022/06/15/929245d2-3a89-445f-996b-623326b4b81f/1a31bddc-febf-451e-a716-773572239804.png) in the left menu. 3. Click **Create Collection**. 4. Name your app if you haven't, and give it a **namespace** (or use the default). 5. Choose **Import from CSV**, then click **Next**.
See how it looks ![Import from CSV](https://wixmp-833713b177cebf373f611808.wixmp.com/images/722864fbafd02b735c1a5b7426ecdfcf.png)
### Configure the imported fields Your new collection is currently empty. Now, let's configure the **Coffee Name** and **Taste Notes** fields to ensure the data imports correctly.
See how it looks ![Empty import](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ceeb248afe76c3ce5f66d2b67c2b8e8e.png)
1. Click **More Actions** > **Import Items**, then select the file you downloaded. 2. In the **Configure Column** dropdown for **Coffee Name**, select **Existing Field**. 3. Verify that **Map to Collection Field** is now set to **Title**. 4. Check the box next to **Taste Notes** to include it in the import. 5. In the **Configure Column** dropdown, select **New Field**. 6. Change **Field Type** to **Text**, then click **Next**.
See how it looks ![Configure field](https://wixmp-833713b177cebf373f611808.wixmp.com/images/909b23f573adfe5ebfcaf14c48c9cc19.png)
7. The **Created Date**, **Updated Date**, and **Owner** fields won’t be imported—that’s fine. Click **Import** to complete the process. ### Change the type of the Taste Notes field The **Taste Notes** field was imported as plain text, but it should be an **Array** containing title-value pairs. Let’s update its type. 1. Click the three dots next to **Taste Notes**, then select **Edit**.
See how it looks ![Change field type](https://wixmp-833713b177cebf373f611808.wixmp.com/images/69dd85eb0a4ac0091fc856beaf711c23.png)
2. Click **Change Type** and scroll down to find **Array**. Select it. Accept the notification - about changing the field type.
See how it looks ![Array type](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a58b8b5a1912f104c849d83313f6d313.png)
3. After making these changes, your collection should look like this:
See how it looks ![Final collection](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5ead907777dd6f63f9900b08fdd08c7d.png)
You can close the collection now. ## Step 5 | Update the `collectionUtils.js` file Since you’re working with a template, update the placeholder collection name in the `collectionUtils.js` file. 1. Open the **Your Collections** tab. 2. Click the three dots next to your collection name. 3. Select **Edit Settings**. 4. Copy your **Collection ID** (e.g., `@user/coffee-catalog/Import360`). 5. Open `collectionUtils.js`. 6. Replace the placeholder text in the `return` statement of the `getCollection()` function with your **Collection ID**. For example: ```ts export function getCollectionName(){ return '@user/my-new-app-167/Import370'; } ```
See where to find collectionUtils.ja ![Final collection update](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f34f5c78cd340552705c5b156685005f.png)
## Step 6 | Load the main repeater Let's load the data from the collection to the main repeater, `productsRepeater`. 1. Go to the **Products** widget code panel.
See how it looks ![product widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ffbdb7291b3bdc7476d00cf5b13f0ed2.png)
2. Delete the boilerplate code and paste the following code, to load the repeater with the data from the collection. ```ts import {getCollectionName, queryCollectionData, populateRepeater} from 'public/collectionUtils' $w.onReady(async function () { populateRepeater($w('#productsRepeater'), await queryCollectionData(getCollectionName())); $w('#productsRepeater').onItemReady(($item, data)=>{ $item('#name').text = data.title || ''; $item('#origin').text = data.originCountry || ''; $item('#price').text = data.price ? `$${data.price}` : ''; $item('#image').src = data.coffeeImage || ''; $item('#tasteNotes1').notes = data.tasteNotes || []; }) }); ```
Note: You have an error in the `notes` property and this is expected. We'll handle it in the following steps.
3. Now click the ![Preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a35d83e13d49f269098b1b571f9e3a7e.png) icon and preview your widget. Since we didn't connect the **Test Notes** widget yet, the notes aren't shown yet. ## Step 7 | Add a property for the Taste Notes widget In order to pass data from the **Products** widget to the nested **Taste Notes** widget, we'll create a new [widget API property](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/widget-api/blocks-widget-properties.md) in the nested widget. We'll name the property `notes`. The data from this property will be used to update the `notesRepeater` and the `rating` repeater that's nested in it. 1. Make sure that the **Taste Notes** widget is selected. 2. Click the **Widget API** ![widget-api](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2021/06/10/9aa25a00-57bb-465d-b443-4e14a5478da3/67b8bc89-773d-4775-a0d5-b1585843da7b.jpg) icon in the widget's code panel. 3. Click **Add New Property**. 4. Name the property `notes`. 5. Set the display name to `notes`. 6. Set the type to `Text`. 7. Click **Create**.
See how it looks ![Create Property](https://wixmp-833713b177cebf373f611808.wixmp.com/images/adfb5dbf243f44d583e4f3b23a33a398.png)
## Step 8 | Add code to handle the `notes` property We’ll now add the code to handle the `notes` property in the `onPropsChanged()` function of the **Taste Notes** widget. This code will: - Transform the incoming `notes` data into a format that the repeater can use. - Split each note into a title and value. - Populate the `notesRepeater` with the transformed data. - Set up the `rating` repeater by creating a list of rating icons based on the note's value. Delete the boilerplate code in the **Taste Notes** widget and copy the following instead: ```js $widget.onPropsChanged((oldProps, newProps) => { const transformedNotes = updateListRepeater(newProps.notes); $w('#notesRepeater').data = transformedNotes; $w('#notesRepeater').onItemReady(($item, data) => { $item('#noteTitle').text = data.note; const ratingIcons = []; for (let i = 0; i < data.value; i++) { ratingIcons.push({ _id: `${data._id}-${i}` }); } $item('#rating').data = ratingIcons; }); }); function updateListRepeater(notes) { return notes.map((item, idx) => { const [note, value] = item.split(':'); return { note, value: parseInt(value), _id: `${idx}` }; }); } ``` Go to **Preview** ![Preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a35d83e13d49f269098b1b571f9e3a7e.png) in the **Products** widget, to see that everything is working.
See how it looks ![preview](https://wixmp-833713b177cebf373f611808.wixmp.com/images/29796ce21d7172da29cdd707c1582903.png)
## Step 9 | Copy and paste the Dashboard IDs Now we'll start managing the Dashboard Pages of the app. [Dashboard pages](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/dashboard-pages/about-dashboard-pages-in-blocks.md) are the back-office of your app, enabling users to manage its data. This app includes two dashboard pages, which we have already designed: - **Manage Products**: Lets users manage all products. - **Product Form**: Lets users manage individual products. Every dashboard page has an ID, which is used in the app code. Let's copy these IDs and paste them in our `constants.js` file. 1. Click the **Dashboard Interface** ![dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/12f55a57377c43ded12075b3be288f03.png) icon. 2. Select the **Manage Products** dashboard page. 3. Hover over the three dots and click **Page Settings**. 4. Copy the **Page ID**.
See how it looks ![Copy ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f7393402b6dd50d564c7dda457afa5f6.png)
5. Click the **Public & Backend** icon **{}** and open the `constants.js` file. 6. Paste the copied Page ID into the appropriate place, replacing the placeholder text in the `constants.js` file. 7. Repeat the process for the **Product Form** dashboard page to copy and paste its Page ID.
See how it looks ![Constants File](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7199e7b217e49633ea16e13be83e0422.png)
## Step 10 | Add code to the Manage Products dashboard page Let's add the code for the **Manage Products** page.
See how it looks ![manage products page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/28040d9f5f025b21d8a62caecd5de87a.png)
1. Select the **Manage Products** dashboard page and open its code panel. 1. First, import the required libraries and constants. These provide access to Wix's dashboard, data manipulation functions, and constants for page navigation and default settings. ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; import { getCollectionName, queryCollectionData, populateRepeater } from 'public/collectionUtils'; import { DASHBOARDS, defaultImage } from 'public/constants'; ``` 1. In the `$w.onReady()`, initializes the page by setting up two actions: - First add an event listener to the **Add Product** button so that when clicked, it navigates the user to the **Product Form** page for adding a new product - Second, query the product data from the database and populates the `productsRepeater` with the retrieved data. ```js $w.onReady(async () => { $w('#addProductBtn').onClick(() => wixDashboard.navigate({ pageId: DASHBOARDS.FORM })); populateRepeater($w('#productsRepeater'), await queryCollectionData(getCollectionName())); }); ``` 1. Still in the `$w.onReady()`, add the `$w('#productsRepeater').onItemReady()` function. This function sets up event handlers for each item in the repeater. The function: - Updates the product details like name, origin, price, and image based on the data. - Attaches click event handlers to the **Edit** button, which navigates to the **Product Form** page with a unique product ID, and the **Remove** button, which triggers the removal of the product. ```js $w('#productsRepeater').onItemReady(($item, data) => { $item('#productName').text = data.title || ''; $item('#productOrigin').text = data.originCountry || ''; $item('#productPrice').text = data.price ? `$${data.price}` : ''; $item('#productImage').src = data.coffeeImage || defaultImage; $item('#editProduct').onClick(() => wixDashboard.navigate({ pageId: DASHBOARDS.FORM, relativeUrl: data._id })); $item('#removeProduct').onClick(() => handleRemove(data._id, data.title)); }); ``` 1. Let's add the `handleRemove()` function. This function: - Removes a product from the database using its ID. - Shows a success toast message. - Updates the repeater by filtering out the removed product from the displayed data. ```js async function handleRemove(productId, productName) { await wixData.remove(getCollectionName(), productId); wixDashboard.showToast({ message: `Product "${productName}" removed successfully`, type: 'success' }); $w('#productsRepeater').data = $w('#productsRepeater').data.filter(item => item._id !== productId); } ```
See the full Manage Products code ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; import { getCollectionName, queryCollectionData, populateRepeater } from 'public/collectionUtils'; import { DASHBOARDS, defaultImage } from 'public/constants'; $w.onReady(async () => { $w('#addProductBtn').onClick(() => wixDashboard.navigate({ pageId: DASHBOARDS.FORM })); populateRepeater($w('#productsRepeater'), await queryCollectionData(getCollectionName())); $w('#productsRepeater').onItemReady(($item, data) => { $item('#productName').text = data.title || ''; $item('#productOrigin').text = data.originCountry || ''; $item('#productPrice').text = data.price ? `$${data.price}` : ''; $item('#productImage').src = data.coffeeImage || defaultImage; $item('#editProduct').onClick(() => wixDashboard.navigate({ pageId: DASHBOARDS.FORM, relativeUrl: data._id })); $item('#removeProduct').onClick(() => handleRemove(data._id, data.title)); }) }); async function handleRemove(productId, productName) { await wixData.remove(getCollectionName(), productId); wixDashboard.showToast({ message: `Product "${productName}" removed successfully`, type: 'success' }); $w('#productsRepeater').data = $w('#productsRepeater').data.filter(item => item._id !== productId); } ```
## Step 11 | Add Code to the Product Form dashboard page Let's add the code for the **Product Form** dashboard page. This form displays an empty form for adding a new product, or a form with the product details for editing an existing product. 1. Select the **Product Form** dashboard page and go to its code panel. 1. First, import some necessary modules and define two global variables, `productId` and `repeaterData`, which will be used later. ```js import wixData from 'wix-data'; import wixDashboard from 'wix-dashboard'; import { getCollectionName } from 'public/collectionUtils'; import { DASHBOARDS, defaultImage} from 'public/constants'; let productId, repeaterData = []; ``` ### Add the `$w.onReady()` function The `onReady()` function initializes the page with the following actions: * The [`observeState`](https://dev.wix.com/docs/velo/apis/wix-dashboard/observe-state.md) function listens for state changes and determines whether to load an existing product or reset the form. When the **Manage Products** page navigates to this form, it either includes a product ID or not. * **If a product ID exists**, the form loads the product data for editing. * **If no product ID exists**, the form remains empty, allowing the user to add a new product. The `onReady()` function handles the button actions according to this logic. Add this code as your `onReady()` function: ```js $w.onReady(() => { wixDashboard.observeState((_, env) => { productId = env.pageLocation.pathname.replace('/', ''); productId ? loadProduct(productId) : resetForm(); }); $w('#cancelBtn').onClick(() => { resetForm(); wixDashboard.navigate({ pageId: DASHBOARDS.MANAGE_ITEMS }); }); $w('#saveBtn').onClick(async () => { await saveProduct(); wixDashboard.navigate({ pageId: DASHBOARDS.MANAGE_ITEMS }); }); $w('#uploadButton').onChange(async () => { handleImageUpload(); }); }); ``` ### Add the `loadProduct()` function Add a `loadProduct()` function, which populates the form's inputs fields with product data. ```js async function loadProduct(id) { const product = await wixData.get(getCollectionName(), id); $w('#productName').value = product.title || ''; $w('#productOrigin').value = product.originCountry || ''; $w('#productPrice').value = product.price || ''; $w('#productImage').src = product.image; setNotesRepeaterData(product.tasteNotes || []); } ``` ### Populate the Notes Repeater Add a `setNotesRepeaterData()` function, which formats and assigns taste notes data to the repeater. The function: - Splits each taste note into note and rating. - Ensures at least three note slots are always available. - Updates the repeater with structured data. ```js function setNotesRepeaterData(tasteNotes) { let parsedNotes = []; if (Array.isArray(tasteNotes)) { parsedNotes = tasteNotes.slice(0, 3).map(noteString => { const [note, rating] = noteString.split(':'); return { note: note || "", rating: parseInt(rating, 10) || 0 }; }); } while (parsedNotes.length < 3) { parsedNotes.push({ note: "", rating: 0 }); } repeaterData = parsedNotes.map((item, i) => ({ _id: String(i), note: item.note || "", rating: item.rating || 0 })); $w('#notesRepeater').data = repeaterData; $w('#notesRepeater').onItemReady(($item, itemData) => { $item('#noteTitle').value = itemData.note; $item('#noteRating').value = itemData.rating; }); } ``` ### Reset the form Add a `resetForm()` function, which clears all form fields and sets a default image. ```js function resetForm() { $w('#productName, #productOrigin, #productPrice').value = ''; $w('#productImage').src = defaultImage; setNotesRepeaterData([]); } ``` ### Save product data Add a `saveProduct()` function. When the **Save** button is clicked, the function: - Gathers all input values from the form. - Converts taste notes into a formatted array ("note:rating"). - If the product already exists (`productId` is set), it updates the record. - If no ID exists, it creates a new product. - Displays a toast notification when the save is successful. ```js async function saveProduct() { const updatedNotes = []; $w('#notesRepeater').forEachItem(($item) => { const note = $item('#noteTitle').value; const rating = $item('#noteRating').value; updatedNotes.push(`${note}:${rating || 1}`); }); const productData = { title: $w('#productName').value, originCountry: $w('#productOrigin').value, price: parseFloat($w('#productPrice').value) || 0, image: $w('#productImage').src || defaultImage, tasteNotes: updatedNotes }; if (productId) { await wixData.update(getCollectionName(), { ...productData, _id: productId }); } else { await wixData.insert(getCollectionName(), productData); } wixDashboard.showToast({ message: 'Product "${productData.title}" saved successfully', type: 'success' }); } ``` ### Handle image upload Add a `handleImageUpload()` function, which uploads the selected file and updates the product image field. If the upload fails, it resets to the default image and shows an error toast. ```js async function handleImageUpload() { if ($w('#uploadButton').value.length === 0) { return; } try { const uploadedFiles = await $w('#uploadButton').uploadFiles(); if (uploadedFiles && uploadedFiles.length > 0) { $w('#productImage').src = uploadedFiles[0].fileUrl; } } catch (error) { console.error('File upload failed:', error); $w('#productImage').src = defaultImage; wixDashboard.showToast({ message: 'Failed to upload image', type: 'error' }); } } ```
See the full Product Form code ```js import wixData from 'wix-data'; import wixDashboard from 'wix-dashboard'; import { getCollectionName } from 'public/collectionUtils'; import { DASHBOARDS, defaultImage} from 'public/constants'; let productId, repeaterData = []; $w.onReady(() => { wixDashboard.observeState((_, env) => { productId = env.pageLocation.pathname.replace('/', ''); productId ? loadProduct(productId) : resetForm(); }); $w('#cancelBtn').onClick(() => { resetForm(); wixDashboard.navigate({ pageId: DASHBOARDS.MANAGE_ITEMS }); }); $w('#saveBtn').onClick(async () => { await saveProduct(); wixDashboard.navigate({ pageId: DASHBOARDS.MANAGE_ITEMS }); }); $w('#uploadButton').onChange(async () => { handleImageUpload(); }); }); }); async function loadProduct(id) { const product = await wixData.get(getCollectionName(), id); $w('#productName').value = product.title || ''; $w('#productOrigin').value = product.originCountry || ''; $w('#productPrice').value = product.price || ''; $w('#productImage').src = product.image; setNotesRepeaterData(product.tasteNotes || []); } function setNotesRepeaterData(tasteNotes) { let parsedNotes = []; if (Array.isArray(tasteNotes)) { parsedNotes = tasteNotes.slice(0, 3).map(noteString => { const [note, rating] = noteString.split(':'); return { note: note || "", rating: parseInt(rating, 10) || 0 }; }); } while (parsedNotes.length < 3) { parsedNotes.push({ note: "", rating: 0 }); } repeaterData = parsedNotes.map((item, i) => ({ _id: String(i), note: item.note || "", rating: item.rating || 0 })); $w('#notesRepeater').data = repeaterData; $w('#notesRepeater').onItemReady(($item, itemData) => { $item('#noteTitle').value = itemData.note; $item('#noteRating').value = itemData.rating; }); } function resetForm() { $w('#productName, #productOrigin, #productPrice').value = ''; $w('#productImage').src = defaultImage; setNotesRepeaterData([]); } async function saveProduct() { const updatedNotes = []; $w('#notesRepeater').forEachItem(($item) => { const note = $item('#noteTitle').value; const rating = $item('#noteRating').value; updatedNotes.push(`${note}:${rating || 1}`); }); const productData = { title: $w('#productName').value, originCountry: $w('#productOrigin').value, price: parseFloat($w('#productPrice').value) || 0, image: $w('#productImage').src || defaultImage, tasteNotes: updatedNotes }; if (productId) { await wixData.update(getCollectionName(), { ...productData, _id: productId }); } else { await wixData.insert(getCollectionName(), productData); } wixDashboard.showToast({ message: 'Product "${productData.title}" saved successfully', type: 'success' }); } async function handleImageUpload() { if ($w('#uploadButton').value.length === 0) { return; } try { const uploadedFiles = await $w('#uploadButton').uploadFiles(); if (uploadedFiles && uploadedFiles.length > 0) { $w('#productImage').src = uploadedFiles[0].fileUrl; } } catch (error) { console.error('File upload failed:', error); $w('#productImage').src = defaultImage; wixDashboard.showToast({ message: 'Failed to upload image', type: 'error' }); } } ```
## Step 12 | Manage Installation Settings Blocks [installation settings](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/configure-blocks-installation-settings.md) determine how your app behaves when installed on a site. Let's remove the **Taste Notes** widget from the **+ Add Elements** panel of a user's editor, since it’s a nested widget. 1. Click on the **Taste Notes** widget name. 2. Hover over the three dots and select **Editor Experience** > **Edit Installation Settings**.
See how it looks ![Installation Settings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8944bd57215360bcc3b48cc0be393213.png)
3. Uncheck the box under **Show in Add panel** to prevent the widget from appearing in the **+ Add Elements** panel.
See how it looks ![Don't Show Widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb6eb8dae2068f15cf1d1187317217f4.png)
## Step 13 | Hide the Product Form Dashboard Page Another step we'd like to configure for better usability is removing the **Product Form** dashboard page from the user's site menu. This form should only be reachable from the **Manage Products** page. To remove the page from the menu: 1. Click on the **Product Form** dashboard page. 2. Hover over the three dots and click **Hide from dashboard menu**.
See how it looks ![Hide Dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1df35c863aa9416bbaa948f9c3086708.png)
## Step 14 | Test Your App Congratulations! Your app is now ready to be tested in the editor or installed on a site. 1. To test your app on a site, click **Test** and select a site to install your app. 1. Add your widget to the canvas. 1. To test the dashboard pages, go to your site's dashboard. It should have a folder with your app's name under **Apps**. Go to the **Manage Products** page and try adding, deleting and editing products. If you want to install the app on other sites or manage its versions, go back to Blocks. Click **Release** to create a major version, then follow the steps to [install it on a site](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/install-a-blocks-app-on-a-site.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Create a Product Widget with Blocks & CLI ## Article: Creating a Product Widget ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-create-a-product-widget-with-blocks-cli.md ## Article Content: # Tutorial: Build a Featured Product Widget with Blocks and the CLI
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md).
**Important:** This tutorial describes the Blocks-CLI integration, which only works with the Wix CLI for Apps. We're introducing a [unified Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md), which we recommend for developing apps and headless projects. The unified Wix CLI doesn't support this integration.
This tutorial walks you through creating a Wix app that displays a featured product widget using the `@wix/stores` SDK module. Here is an image of how the widget looks on a site: ![widget on site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d46f141e7f24502665dc6a1e4877c705.png) ## Before you begin Before getting started, make sure that: + You have [Node.js](https://nodejs.org/en/) (version 20.11.0 or higher). + You’re logged into your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). ## Step 1 | Set up your environment First, let's set up a development site, create the app and add the app to the site. ### Create a Site To create a development site: 1. Go to the [eCommerce templates](https://www.wix.com/studio/templates/ecommerce) page. 1. Select a site template and click **Edit**. This creates your own copy of the site. 2. Publish the site. ### Create an App To create your app: 1. Open your terminal. 1. Run: ```bash nvm use 20.11.0 npm create @wix/app@latest ``` 1. When asked what you would like to do, select **Create a new Wix App**. 1. When asked how you would like to create your new app, select **Create a basic app**. 1. Enter a name for your app. 1. Enter a project folder name. 1. Wait as the CLI creates the files for your project. 1. Navigate to the project folder: ```bash cd ``` 1. Run the generate command: ```bash npm run generate ``` This will display a list of extensions that you can add to your app. 1. Select **Site Widget**. 1. When asked which framework to use, select **Blocks**. 1. When asked if you'd like to install dependencies, type **Y**. 1. After the CLI finishes installing dependencies, install the the Wix Stores package: ```bash npm install '@wix/stores' ``` > **Note:** You can ignore the warnings in your terminal. ### Install your app on a Development Site 1. Run the following command to start up a local development server: ```bash npm run dev ``` 1. When asked to select a Development Site, select **Pick an existing site**. 1. Select the site you just created. 1. Press **Enter** to install your app on the site. 1. Click **Agree & Add** in the browser that opened. 1. Go back to your terminal and type **B** to open the Blocks editor. ## Step 2 | Design your widget in the Blocks Editor Now you are in the Blocks Editor, where you'll design your widget. 1. Click **Create Extension** > **Widget**. 1. Search for the **Featured Item** widget. 1. Select the widget and click **Add Widget**. ![featured item](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e5c7a06332dd5a734c806d8345bd75ca.png) 4. Delete the first widget, **Widget1**, by clicking the three dots next to its name in the **Widgets** menu. 5. Click **Sync** and then **Sync Changes** to confirm. This syncs your app's UI to your local code files. ![ready widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/12e6aa20569275b02ea17b9799f1878b.png) ## Step 3 | Add your widget to the site 1. In the CLI, type **E** to open the site in the Editor of your Development Site. Once the site opens, your app is added to the site. 1. Click **+ Add Elements** > **App Widgets**. 1. Click on the **Featured Item** widget to add it to the canvas. ![drag widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2c55aa73afe576e78349be9c9e31b834.png) The widget is now located on the page and uses the site theme to match look and feel of the site. ## Step 4 | Implement the widget logic In this step, you’ll define the widget logic, which presents data according to the product ID. ### Define the widget API in `api.ts` Define a property in the widget API, to hold the product ID. 1. Open the `src` folder of your project. 1. Go to the file: `site/widgets/blocks/featured-item/api.ts` 1. Delete the default event, `load`. 1. Delete the default function, `myPublicFunction()`. 1. Change the property name of the property in the `props` array from `name` to `productId`. 1. Save the file. Your code should look like this: ```ts import { defineAPI, WidgetPropertyType } from '@wix/blocks'; export default defineAPI({ props: { productId: { type: WidgetPropertyType.STRING, }, }, events: {}, functions: {}, }); ``` ### Use the property to present data in the `widget.ts` The `widget.ts` in the same folder is where you add your widget's business logic. You'll use the property you defined in the `api.ts` to present the data of the selected product. 1. Import the SDK `stores` module: ```ts import { products } from '@wix/stores'; ``` 2. Define a helper function `updateProduct` inside the `defineWidget()` callback that fetches product data using the widget's `productId` property and updates the title, subtitle, and image elements accordingly. ```ts const updateProduct = async () => { const { product } = await products.getProduct($widget.props.productId); $w('#title').text = product?.name!; $w('#subtitle').text = product?.description!; $w('#imageX1').src = product?.media?.mainMedia?.image?.url!; }; ``` > **Note:** The `?` and `!` are used because the stores API can also return `undefined`. 3. Replace the boilerplate code of the `onReady()` and `onPropsChange()` functions. Paste the following code instead, to call the `updateProduct()` function. Make sure that all these functions are within the `defineWidget()` function. ```ts $w.onReady(async () => { await updateProduct(); }); ``` ```ts $widget.onPropsChanged(async (oldProps, newProps) => { await updateProduct(); }); ``` 4. Delete the boilerplate function, `myPublicFunction`, from the return statement. 5. Save the file. ## Step 5 | Design the Settings Panel The **Featured Item** widget that you added to the app comes with a default settings panel. Let's modify this panel. 1. Go back to your open **Blocks** editor. 1. Click the **Editor Experience** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/04/24/d885478b-0fa3-46d0-950a-801399d94383/8dc747a0-244b-4714-bd4c-7a71986a8394.png) icon. 1. Click **Custom Panels**. You can see that the **Featured Item** widget has one custom panel, named **Settings**. 1. Delete the toggle and text elements. 1. Click **+ Add Elements** and add a dropdown element. ![add elements to panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a89ab094914fddbc6e58aae558ce28b1.png) 1. Click the **Settings** icon and change the **Field title** to **Choose product** (or any other title you want). 1. Click **Sync** to update your UI to your code files. ## Step 6 | Add the panel logic in the **`panel.ts`** Let's implement the Settings panel logic. The panel gets the list of products and maps them to the dropdown. When the dropdown selection changes, the panel sets the property in the widget API. When the panel opens, it presents the product that was set in the widget API property. If no property was set, the panel presents the first product in the list. 1. Go to: `site/widgets/blocks/featured-item/panels/settings/panel.ts` 1. Add the following import: ```ts import { products } from '@wix/stores'; ``` 3. Replace the `onReady()` function with this code, which gets the Stores product and sets it to the widget’s `productId` property. ```ts $w.onReady(async () => { const { items } = await products.queryProducts({}); $w("#panelDropdown1").options = items.map((item: any) => ({ label: item.name, value: item._id, })); $w("#panelDropdown1").value = (await widget.getProp("productId")) ?? items[0]._id; $w("#panelDropdown1").onChange(async (event: any) => { await widget.setProp("productId", $w("#panelDropdown1").value); }); }); ``` 4. Save the file. ## Step 7 | Add a price element to the widget Back in the Blocks editor, let's add a text element to present the product price. To do this, you'll need to add a paragraph to the widget's stack. 1. Go back to the **Featured Item** widget in the **App Interface**. 1. Right click on the widget stack, which brings together the **Item Title**, the description and the button. 1. Click **Quick Add**. 1. Click **Paragraph**. ![add-paragraph](https://wixmp-833713b177cebf373f611808.wixmp.com/images/35e94df27dd0025f4f001aa72b1f7bbc.png) ### Make some design changes 1. Change the content of the paragraph you just added to be a price. For example, **75$**. 1. Make sure that the paragraph is placed in the third row of the stack (use the **Move up** and **Move down** errors if needed.) 1. Align the paragraph to the left. 1. Change the font size to 16. 1. Reduce the size of the stack gaps. 1. Change the button text to **More Info**. Your finished widget should look like this: ![see finished widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/41f3abc8926bd3ea9e08c7c780c3095d.png) > **Note:** The design changes are not necessary for the app to function. ### Rename the ID of the element you added Let's rename the new element, so that you can refer to it in your code. 1. Make sure the text element is selected. 1. Click the **Layers** icon. 1. Click the three dots next to the `#text1` element. 1. Click **Rename** and rename the element to `price`. 1. Click **Sync**. ![rename to price](https://wixmp-833713b177cebf373f611808.wixmp.com/images/dae72937462211a7c2f50fb539af01a7.png) ### Add the price to the `updateProduct()` function Let's update the `updateProduct()` function in the `widget.ts`. 1. Add this line under the other lines in the function. ```ts $w('#price').text = product?.priceData?.formatted?.price!; ``` 1. Save the file. ## Step 8 | Test Your Widget It's time to test the widget in the editor and see the product information update when the panel changes. 1. Go back to the editor to see the new price element (you can type **E** if you closed it). 1. Click the widget's **Settings** action button. 1. Select various products from the dropdown and see that the widget information updates as expected. ## Step 9 | Add a button click behavior Let's add the logic for the button to lead the user to the product page. 1. Press `Ctrl + C` in the terminal, to stop the development server. 1. Run: ```bash npm install '@wix/site-location' ``` > **Note:** You can ignore the warnings. 3. In the `widget.ts` file, add: ```ts import { location } from '@wix/site-location'; ``` 4. In the `updateProduct()` function, add: ```ts $w('#exploreButton').onClick(() => { location.to(product?.productPageUrl?.path!); }); ``` 5. Save the file. You'll be able to test the button on a live site after releasing a version of the app. ## Step 10 | Release the app to create a version Now that you're done developing your app, you can create a version. 1. Run: ```bash npm run build npm run release ``` 1. Run dev again: ```bash npm run dev ``` 1. Click **S** to open the site. 1. Go to the widget. 1. Click the button — it should navigate to the product page. Congratulations, you’ve created and connected a fully functional, data-powered featured product widget using Blocks and the CLI! --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 1 | Create a Custom Wix app ## Article: Step 1 | Create a Custom Wix app ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-1-create-a-custom-wix-app.md ## Article Content: # Step 1 | Create a Custom Wix app > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/overview.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md) The first thing we’ll do is create an app in the [Custom Apps](https://manage.wix.com/account/custom-apps?referralInfo=sidebar\&shownApps=%5B%7B%22id%22%3A%22activeApps%22%2C%22name%22%3A%22All+apps%22%7D%5D\&sort=last-update+desc\&selectedColumns=name%2Capp-status%2Clatest-version%2Clast-update%2Cinstallations%2Crating%2Cdate-created+false%2Ccreated-by+false) page. The app dashboard acts as a control center for the various pieces we'll add to our app–it allows us to manage extensions, permissions, installation settings, and more. We’ll touch on a lot of this later in the tutorial. For now, we only need to do 2 things in our app dashboard: 1. Locate the app ID, public key, and app secret. These will become important a bit later when we write our server code. On the [home](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome) page of the app dashboard, in the top right corner click the **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2219a1394b03e02f815f43a1f8e97e66.png) button and choose **View ID & keys**. ![More Actions > View ID & keys](https://wixmp-833713b177cebf373f611808.wixmp.com/images/290f6b3cb535aac99c2a9059576ab3d1.png) This opens a modal called **App ID & keys** that contains your app secret key, app ID, public key, and namespace. You don’t need to do anything with these values just yet, but remember this location for later. 1. Set up our app to automatically install Wix eCommerce. In order for our business solution to take advantage of eCommerce services like cart and checkout, Wix eCommerce must be installed on a Wix user’s site. Setting up automatic installation of Wix eCommerce ensures our app will function correctly without the Wix user having to take extra action. [Follow these instructions](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/get-started/apps/build-a-business-solution.md#step-1--set-up-automatic-installation-of-the-ecommerce-app) to configure your app to automatically install Wix eCommerce. Now that we’re familiar with our app dashboard, let’s start building our app. In the next couple of article, we’ll be working in [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md). **Next up:** [Step 2 | Create a Catalog Database in Blocks](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 2 | Create a Catalog Database in Blocks ## Article: Step 2 | Create a Catalog Database in Blocks ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md ## Article Content: # Step 2 | Create a Catalog Database in Blocks > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-1-create-a-custom-wix-app.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md) In order to integrate with the Wix eCommerce platform, your business solution must provide a catalog of items for sale. In general, the catalog database can be hosted and managed wherever you prefer. In our example app, each site’s catalog database will live on the site itself. A catalog database allows users to store information about the products they want to sell, and is central to any eCommerce app. For our example business solution, we’ll create a catalog of poems as a [Wix data collection](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/cms-collections-in-blocks/about-cms-collections-in-blocks.md). When Wix users install our app, a copy of the collection is installed on their site. Wix users can then customize this collection to meet their site’s needs. To create a collection in Blocks, we first need to connect our app to Blocks. We can do this by creating a Blocks widget from the app dashboard. 1. To add a widget, return to your app dashboard and go to [extensions](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fextensions). Click on **+ Create Extension**. ![Extensions tab](https://wixmp-833713b177cebf373f611808.wixmp.com/images/cf3422b83cad9ff525ac1953096a257d.png) 1. In the modal that opens, search for “widget” and find the Blocks widget extension. Click **+ Create on Blocks**. This opens a new tab in the Blocks editor. ![Create widget on Blocks](https://wixmp-833713b177cebf373f611808.wixmp.com/images/38eecd8efcf7bcf32134009325bd90a5.png) > **Note:** If a window pops up prompting you to choose a template, you can close it. In the lefthand column of the Blocks editor, you should now see something that looks like this: ![App interface widget 1](https://wixmp-833713b177cebf373f611808.wixmp.com/images/405e69e39f7928ac12ec14a07d99dad8.png) We’ll rename and design our widget in the next step, but let’s first add the product catalog. 1. In the menu on the left, click **CMS** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/82123f77f0c38ce1ce3702a8fc82d820.png). ![Click CMS](https://wixmp-833713b177cebf373f611808.wixmp.com/images/baca8329adcc3d267efe3411a6feb3fd.png) 1. Under **Your Collections**, click **Create Collection**. A modal opens prompting you to name your app. Click **Save & Continue** to save your app name. ![Create your first collection](https://wixmp-833713b177cebf373f611808.wixmp.com/images/91dcc83e013cf6b050977ed527635abe.png) 1. Next, Blocks prompts you to create an app namespace. Wix uses this namespace to identify your apps and create the collection ID. Click **Next** to continue. A new modal that looks like this appears: ![Create app namespace modal](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e1b0ea937a7694d02349b68664626824.png) The usual format of the namespace is @`accountName`/`appName`. We recommend accepting the default format. Click **Next** to continue. 1. Once you’ve set up your app name and namespace, a **Create a collection** modal appears offering you several options on how to set up your initial collection. Select the **Start from scratch** option and click **Next**. ![Create a collection modal](https://wixmp-833713b177cebf373f611808.wixmp.com/images/aae87f90ea19b0a38e1227d4d9550df9.png) 1. Name your collection **Poems** and click **Create**. Blocks opens the CMS, where you can start editing the collection structure. 1. Click **+ Add Field** to add columns to the collection. Add the following columns to the collection: | **Field name** | **Field type** | | --------------- | ---------------- | | Title | text | | Price | text | | Description | text | | Main Product ID | text | | Variants | Javascript array | Wix generates the field ID automatically when you name the field. Accept the default IDs. > **Note:** Once you save the field, you can’t change the field ID. 1. Now let’s add some sample data to get us started. Add the following items to the collection: | | | | | | | --------------- | --------- | --------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Title** | **Price** | **Description** | **Main Product ID** | **Variants** | | Birthday Poem | 3 | Write a poem for someone’s birthday | 00003 | [  {    "label": "For partner",    "value": "v01"  },  {    "label": "For child",    "value": "v02"  },  {    "label": "For friend",    "value": "v03"  }] | | Wedding Poem | 2 | Congratulate the bride or groom with a wedding poem | 00002 | [  {    "label": "For bride",    "value": "v01"  },  {    "label": "For groom",    "value": "v02"  }] | | Graduation Poem | 1 | Celebrate a new graduate with a graduation poem | 00001 | [  {    "label": "For child",    "value": "v01"  },  {    "label": "For other relative",    "value": "v02"  }] | When a Wix user installs the app, they’ll receive these default values in their Poems collection. This completes our catalog database setup. In the next article, we’ll connect our widget to the collection to allow site visitors to view the available poems. **Next up:** [Step 3 | Create an Item Page in Blocks](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 3 | Create an Item Page in Blocks ## Article: Step 3 | Create an Item Page in Blocks ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md ## Article Content: # Step 3 | Create an Item Page in Blocks > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md) An item page is part of the app’s frontend interface that customers interact with. It allows customers to select the items they want and directs them to the cart and checkout pages. You must create at least one [item page](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/get-started/apps/develop-a-business-solution-item-page-with-blocks.md) for your app that implements eCommerce functionality. In this example, our item page will take the form of a widget that we can add to a site page. The widget will allow a customer to select a poem type from the site catalog, choose a variant of the poem, and set the poem length. We’ll edit the widget we created in the previous step to build a simple interface that provides this functionality. 1. Go to the **App Interface** page of the Blocks editor. In the column on the left side of the editor, click the **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png) button and rename your widget to “Item selector”. ![Rename widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/be7c7ca5b2f2d25c75ab259c6e13ef47.png) 1. Add the following elements to the widget and set their element IDs according to this table: | | | | --------------- | ---------------- | | First dropdown | #selectPoemType | | Second dropdown | #selectVariant | | Text input | #numLines | | Button | #addToCartButton | You can change the element ID by selecting the element and then going to the **Properties & Events** panel. ![Change the element IDs in the Properties & Events panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e68a0f840fb2c345ea196f1833d1a44d.gif) 1. Select each element and click **Settings** to adjust the field title or displayed text. Your widget should look like this when you’re done: ![Completed item page widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0304d2bded037c089976077927346369.png) 1. Now we need to add functionality to the widget. First, we want the top dropdown element (`#selectPoemType`) to display the titles of the poems in our collection. We can do this easily with a [dataset](https://dev.wix.com/docs/velo/velo-only-apis/$w/dataset/introduction.md). Start by selecting the dropdown. In the action bar, click the **Connect to CMS** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/02/27/0529fe32-8e09-4f9b-be94-38010664296d/82eea2f5-fe57-423a-a0e3-78bfc698c647.png) icon. Because we haven’t created a dataset yet, Blocks prompts us to create one in the menu on the right: ![Add your first dataset](https://wixmp-833713b177cebf373f611808.wixmp.com/images/047eb6779fbc9e436646989e7a7be327.png) Click **+ Add a Dataset** and select the Poems collection. Accept the default dataset name and click **Create**. The menu on the right displays 2 options for how the dropdown will be used; select the **Filter content** option. ![Select Filter content option](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ce158b98fd140c1980242d67fb7e6bdd.png) Under **Connections** > **Filter content by**, select the **Title** field from the dropdown menu. Leave the condition as **Is**. The `#selectPoemType` dropdown is now connected to the **Title** field in the dataset collection, and will display all available poem titles when opened. You can preview this behavior by clicking **Preview** in the top right corner of the editor. 1. We want the second dropdown (`#selectVariant`) to display the possible variants of the selected poem type. This means that the options in the second dropdown depend on the value of the first. This behavior is too complicated for a dataset, so we’ll create it in the widget code. Open the code panel at the bottom of the editor. Delete the default code and start by adding the following: ```js import wixData from 'wix-data'; import {currentCart} from 'wix-ecom-backend'; import wixEcomFrontend from "wix-ecom-frontend"; $w.onReady(function () { $w('#selectVariant').hide(); $w('#numLines').hide(); $w('#addToCartButton').hide(); }); ``` Here’s a quick explanation of the code we just added: **Lines 1-3**: Import several modules that we need to make our widget functional. The most important to us right now is the `wixData` module, as we’ll use this to retrieve the correct options for the second dropdown. **Lines 5-9**: In the widget’s `onReady()` function, hide all elements except for the first dropdown. Once the customer selects a poem type, we’ll display the remaining elements. Whenever a user makes a selection in the `#selectPoemType` dropdown, we want to update the second dropdown to display the variants for that poem type. To set this behavior, we use the [onChange()](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/on-change.md) event handler as shown below: ```js $w('#selectPoemType').onChange(async (event) => { try { const result = await wixData .query("") .eq("title", event.target.value) .find(); const item = await result.items[0]; $w('#selectVariant').options = item.variants; $w('#selectVariant').show(); $w('#numLines').show(); $w('#addToCartButton').show(); } catch(error) { console.log(error); } }) ``` Let’s break down this code: **Lines 3-8**. Query the collection for the new value of the first dropdown, and store the result. This requires the collection ID, which you can find by opening the CMS and going to the collection settings. **Line 10**. Set the dropdown [options](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/options.md) for the second dropdown to the variants that correspond to the selected poem title. This is made easy by the fact that we created the **Variants** field in the collection as a Javascript array in the format necessary for dropdown elements. **Lines 11-13**. Display the remaining elements. The widget now displays the poem options stored in the collection to customers. Click **Preview** in the top right corner of the editor to test out this behavior and make sure everything works as expected. 1. Once the user has selected the poem they want, they should be able to add it to their cart and navigate to the cart page. To set this behavior in our widget, we’ll make use of the 2 eCommerce modules we imported at the top of our code. The first thing we do is create a function `addToCurrentCart()` which wraps the actual Wix eCom function. Beneath our `onChange()` logic, add the following function: ```js async function addToCurrentCart(lineItems) { return await currentCart.addToCurrentCart(lineItems); } ``` Inside the function, we call the [addToCurrentCart()](https://dev.wix.com/docs/sdk/backend-modules/ecom/current-cart/add-to-current-cart.md) eCom method and pass it an object called `lineItems`. This object contains a catalog reference and quantity for each item that the customer has requested. We want to call `addToCurrentCart()` when a customer clicks the **Add to cart** button. Beneath the function we just wrote, add an `onClick()` handler like this: ```js $w('#addToCartButton').onClick(async (event) => { try { const result = await wixData .query("") .eq("title", $w('#selectPoemType').value) .find(); const item = result.items[0]; const itemId = item.mainProductId; const variantId = $w('#selectVariant').value; await addToCurrentCart({ lineItems: [{ catalogReference: { appId: "", catalogItemId: itemId, options: { audience: variantId } }, quantity: parseInt($w('#numLines').value) }] }) await wixEcomFrontend.refreshCart(); wixEcomFrontend.navigateToCartPage(); } catch(error) { console.log(error); } }) ``` Let’s break down the code in the `onClick()` to understand it: **Lines 3-6**. Query the collection for the value of `#selectPoemType` again. The purpose of this query is to get the ID of the selected poem. **Lines 8-9**. Store the result of the query in `item` and parse it for the poem’s product ID, which we store in `itemId`. **Line 10**. Get the selected variant from the second dropdown `#selectVariant`. **Lines 12-23**. Call `addToCurrentCart()` and pass it an object in the correct format. The nested `catalogReference` object requires our app ID and the product ID of the poem. Recall that we can find the app ID in the [home page](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome) of our app dashboard. For the poem’s ID, we pass the `itemId` variable. We also pass an options object to define the variants. In our solution, variants are defined by whoever is receiving the poem, so we define a single attribute `audience` and give it the value we took from the `#selectVariant` dropdown. Finally, we take the number of lines requested by the customer and pass that as the quantity. **Lines 24-25**. Call the frontend Wix eCommerce method [`refreshCart()`](https://dev.wix.com/docs/sdk/frontend-modules/ecom/open-side-cart.md) to update the cart with the latest product. Then call [`navigateToCartPage()`](https://dev.wix.com/docs/sdk/frontend-modules/ecom/navigate-to-cart-page.md) to send the customer to the cart page, where they’ll see a summary of their order. > **Note:** You can’t preview the Wix eCommerce methods in the Blocks editor. To test the behavior of eCom methods, install your app on a test site. This completes the design and code for the item page widget. In the next article, we’ll create the dashboard for the Wix user. **Next up:** [Step 4 | Create a Dashboard Page to Manage the Catalog](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 4 | Create a Dashboard Page to Manage the Catalog ## Article: Step 4 | Create a Dashboard Page to Manage the Catalog ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md ## Article Content: # Step 4 | Create a Dashboard Page to Manage the Catalog > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md) Instead of requiring the Wix user to enter the CMS and directly edit the Poems collection, we’ll create dashboard pages where they can add and remove poems. This is a more comfortable interface, especially for a user who doesn’t work frequently with databases. In Wix Blocks you can create [dashboard pages](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/dashboard-pages/about-dashboard-pages-in-blocks.md) that are added to the dashboard of a Wix site upon installation. These pages appear under the **Apps** page in a Wix user’s dashboard. To add a dashboard page in the Blocks editor: 1. In the menu on the left, click on the **Dashboard Interface** ![dashboard interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f03d7f8b37810a8807182a4e4da95f39.png) icon and click **+ Create Extension**. Select the **Dashboard page** option. 1. Blocks opens a new dashboard page for you in the editor. Next to the title of the dashboard page, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d8e1fc0ff6e1bd5c4ced7cdf3992b87.png) > **Rename** and change the name of the page to **Poems Manager**. The **Poems Manager** page already has some basic layout elements. You can see these elements more clearly by clicking the **Layers** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ee8c8ae7626cd241a98acdef80723868.png) icon in the left menu. 1. Hover over each of the elements in the page to understand which label corresponds to what. First, locate the label that corresponds to the title. Click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d8e1fc0ff6e1bd5c4ced7cdf3992b87.png) > **Rename** and rename the box to `pageTitle` so that we can distinguish it later. This first dashboard page will display the poems in the catalog database, and allow the Wix user to delete poems. So let’s give it a fitting title and subtitle: - **Title**: Poems Manager - **Subtitle**: Add and remove the poems your site offers 1. Delete the **Secondary Action** button, and edit the **Main Action** button settings to change the button text to **Add a poem**. Change the element ID of the **Add a poem** button to `#mainAddPoemButton`. ![Change the text on the button to add poem](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fee6fcdb03412f3a229e34c1d78e62e5.png) The upper box on the dashboard page should now look like this: ![Poems manager dashboard page title](https://wixmp-833713b177cebf373f611808.wixmp.com/images/38a9d556dd3dce6977e975ee741292dc.png) 1. Now locate the label that corresponds to the card on the dashboard page. ![Poems manager card](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2632668ae36a7dfdc02308f3f4e19cfb.png) In general, we want the **Poems Manager** dashboard page to display the poems listed in the product catalog. However, if the catalog is empty, it should instead display a message prompting users to add a poem. Since our dashboard page will look different depending on the state of the collection, we need to incorporate that state into the card by using a [multi-state box](https://dev.wix.com/docs/velo/api-reference/$w/multi-state-box/introduction.md). Next to the box that corresponds to the card, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d8e1fc0ff6e1bd5c4ced7cdf3992b87.png), and select **Place in Multi-State Box**. ![Place in multi-state box](https://wixmp-833713b177cebf373f611808.wixmp.com/images/933350d986006f4758d27a571b374ef4.png) In the box itself, first delete the subtitle and edit the title to say **Your Poems**. This can remain the same in both states. Now rename the box `emptyState` to distinguish it from the opposite state. In the lefthand menu, click **Add Elements** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a84ed686eab15ec0bb4bf7d469f62590.png )> **Design Patterns** > **Empty States** and select the **Add your first product** option. ![Choose Add your first product empty state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ad75f273c3fbaa7eb13da3776e14a429.png) Edit the text and the button so that the empty state element looks like this: ![Empty state title and button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e1cfe3b9e8c065965722ccc2d2fc1b9c.png) Then, select the button and change its ID to `#addFirstPoemButton` to distinguish it from the **Add a poem** button at the top of the page. This completes the dashboard UI for the empty state. We’ll add code shortly. Let’s create the second state next. 1. Add a second box to the multi-state box and rename it `poemsList`. Again, delete the subtitle and edit the title of the box to **Your Poems**. For this state, we’ll use a [repeater](https://dev.wix.com/docs/velo/api-reference/$w/repeater/introduction.md) to display poems in the collection. Go to **Add Elements** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a84ed686eab15ec0bb4bf7d469f62590.png) > **Layout** > **Repeaters** and select the 4-square repeater. ![4-square repeater](https://wixmp-833713b177cebf373f611808.wixmp.com/images/56021b9784eeff3a5c13b6aa8ae3eaf2.png) Adjust the repeater’s position on the page. In the **Properties & Events** panel, change the repeater ID to `listOfPoems` and the ID of each card to `poemCard`. Then add the following elements inside the cards: - A Heading 2 text element - A Heading 4 text element - An icon button like this: ![Trash can button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/af3f198ba475bff6be777d2b77fbe5e0.png) 1. We can add functionality to the `poemsList` state simply by connecting it to the CMS. Select the repeater, and in the action bar click **Connect to CMS** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/02/28/35acad32-03ed-4ea0-a201-df5ebcc37ceb/eb371f17-e215-41bc-9260-64ac656eade4.png). Though we already created a dataset for the item page, that dataset doesn’t apply to the dashboard interface. Therefore, we need to create a new one. In the menu on the right, click **+ Add a Dataset** to create a new dataset. Select the **Poems** collection as the one we want to connect to, and name the dataset **Poems Dashboard** so we can distinguish it from the item page dataset: ![Create a dataset for the dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2e42c146516b5d7f28e3e005742c9590.png) Click **Create**. The righthand menu displays the new dataset. We first need to edit the dataset settings to allow the dashboard to write to the collection. From the righthand menu click **Dataset Settings**. ![Choose the dashboard dataset settings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/841042fab47bc5fd8ace8b69325f6b59.png) Set the dataset mode to **Read & Write**. ![Set dashboard dataset mode to Read & Write](https://wixmp-833713b177cebf373f611808.wixmp.com/images/e4daa6034fd3f6761fff86842d8e5c67.png) Now connect the elements in the repeater card as follows: * Heading 2 text: Title * Heading 4 text: Description * Icon button: Under **Connections** > **Click action connects to**, select **Delete**. Our nonempty state now can display poems from the collection and delete them. Next we’ll create the capability to add poems from the dashboard. 1. In order to add poems to the collection, we’ll create an entirely new dashboard page. Return to the **Dashboard Interface** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f03d7f8b37810a8807182a4e4da95f39.png) section and at the bottom of the menu click **+ Create Extension**. 1. Rename the new dashboard page to **Add poem**. We actually don’t want this dashboard page to be visible in the menu–we only want a Wix user to access it from the **Poems Manager** page. To set this behavior, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d8e1fc0ff6e1bd5c4ced7cdf3992b87.png) > **Hide from dashboard menu**. 1. To set up the UI for the **Add poem** page, first edit the existing elements as follows: - State 01: **Add a poem** - Page subtitle: delete - Card title: **Poem Details** - Card subtitle: delete - Main Action button: Change text to **Save** and change the ID to `#savePoemButton`. - Secondary Action button: Change text to **Cancel** and change the ID to `#cancelButton`. When you complete these changes, the top of your dashboard page should look like this: ![What your add a poem dashboard page should look like](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d9ec9afda7fe6b6e59931f7daf9ed4c.png) 1. Now we’ll create a form that will allow the user to add a poem’s details. To do so, add the following elements in the order shown and set their IDs in the **Properties & Events** panel: | | | | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ---------------- | | Element type | ID | Text | | Text Input | #poemTitle | Poem title | | Text Input | #poemPrice | Price per line | | Text Input | #poemDescription | Poem description | | Text Input | #poemId | Catalog ID | | Upload **+ Add Product** Button ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/97c8407ab369e478e6a22093cea3f238.png) | #addVariantButton | Add Variant | Beneath these elements, add a repeater: ![Select repeater with 3 horizontal boxes](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f0ee9f13e8540ceb14423a15c279bfa5.png) We’ll use this repeater to give the Wix user control over how many variants they want to add. Adjust the size of the repeater to align with the other elements, and change the repeater’s ID to `variantsList`. With the repeater still selected, in the right menu edit the number of elements: ![Manage repeater items](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7c15004896d688a201130d54d5435fce.png) We want to start with just one element in the repeater, so delete 2 of the boxes: ![Delete boxes from repeater](https://wixmp-833713b177cebf373f611808.wixmp.com/images/37b3890bf1142b4602444ec8855dc13b.png) Add the following elements to the remaining box in the repeater: | | | | | --------------------- | -------------------- | ------------- | | **Element type** | **ID** | **Text** | | Text Input | #variantLabel | Variant label | | Text Input | #variantId | Variant ID | | Trash can icon button | #deleteVariantButton | | The complete UI should look like this: ![Full add a poem dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7c98fc4763e0509080952c19de394d31.png) Now that we’ve designed the dashboard UI, in the next section we’ll add the code that will enable the user to actually add a new poem to the collection. ### Dashboard code Before we dive into the code, let’s consider exactly what we need the code to do: - Insert the user inputs into the collection when the user clicks **Save**. - Return to the **Poems Manager** page after the user clicks either **Save** or **Cancel**. - Add new fields for variant information each time the user clicks **+ Add Variant**. - Delete a set of variant fields when the user clicks `#deleteVariantButton`. As it turns out, the trickiest part of the code will be controlling the UI to add and remove variants, so we’ll start with that. 1. Open the code panel for the **Add poem** page. Delete the existing code and add the following: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; $w.onReady(function () { let variantsArray = []; $w('#variantsList').hide(); }); ``` **Lines 1-2**. We import 2 packages for handling data and moving between dashboard pages. These packages aren’t necessary for the repeater code but will become important later. **Line 5**. Inside the `onReady()` handler, we first create a blank array called `variants` array. We’ll use this array to set the `#variantsList` repeater [data](https://dev.wix.com/docs/velo/api-reference/$w/repeater/data.md), which will control the number of variants that appear in UI. **Line 6**. Hide the variants list when the **Add poem** page first loads. 1. Next, we add the logic to add a variant: ```js $w("#variantsList").onItemReady(($item, itemData, index) => { $item("#variantId").value = itemData.value; $item("#variantLabel").value = itemData.label; $item('#deleteVariantButton').onClick( () => { variantsArray.splice(itemData.realIndex,1) variantsArray.forEach((item, index) => { item.realIndex = index; }); $w('#variantsList').data = variantsArray; }) }); $w('#addVariantButton').onClick( () => { if ($w('#variantsList').hidden) { $w('#variantsList').show(); } let highestId = 0; variantsArray.forEach((item, index) => { if(Number(item._id) >= highestId) highestId = Number(item._id) + 1; }); const newItem = { _id: highestId.toString(), value: '', label: '', realIndex: variantsArray.length }; variantsArray.push(newItem); $w('#variantsList').data = variantsArray; }); ``` Let’s start by breaking down the `onClick()` handler code first: **Lines 14-16**. Unhide the repeater in case it was still hidden. **Lines 18-21**. Perform a calculation to track the correct index. This helps later if we need to delete a variant. **Lines 23-28**. Create a new item to insert into the array. The `value` and `label` fields, which correspond to the 2 input fields in the repeater item, are blank. The result is that a new set of blank fields is added to `#variantsList`. **Lines 29-30**. Push the new item to `variantsArray` and set the repeater data equal to the updated array. This triggers the [`onItemReady()`](https://dev.wix.com/docs/velo/api-reference/$w/repeater/on-item-ready.md) handler above. This brings us back up to the top of the code: **Lines 2-3**. Create the 2 new input fields with blank values. **Lines 4-10**. Handle the behavior for when the `#deleteVariantButton` is clicked. Remove the selected item from `variantsArray` and set the repeater data equal to the reduced array. The end result of all this logic is behavior like this: ![Example of the add variant behavior](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a493faf010fcb2e7732a846976ad6983.gif) 1. Let’s move on now to the part of the code that controls the save and cancel actions. When both of these actions complete, we want to navigate the user back to the **Poems Manager** dashboard page. Previously, we imported the `wix-dashboard` package. In Blocks, we can use the [`navigate()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/navigate.md) function from this package to open a different dashboard page from our current one. Let’s start with the code for the **Cancel** button as it is simpler. When the user clicks the **Cancel** button, we don’t need to store anything. We can simply navigate back to **Poems Manager**: ```js $w('#cancelButton').onClick( ()=> { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }) }) ``` The only piece of information we need here is the ID of the page we want to go to, in this case **Poems Manager**. To find this ID, open the **Dashboard Interface** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f03d7f8b37810a8807182a4e4da95f39.png) and click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9d8e1fc0ff6e1bd5c4ced7cdf3992b87.png) > **Page settings** next to **Poems Manager**. Next to **Page ID** click **Copy** and close out of the modal. Paste the copied ID as the value for the `pageId` parameter. We can start the **Save** logic in a similar manner: ```js $w("#savePoemButton").onClick(() => { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }); }); ``` However, when we save we also need to store the details that the user input. To do so, add the following code before `navigate()`: ```js const data = []; $w('#variantsList').forEachItem(($item, itemData, index) => { data.push({ _id: index.toString(), label: $item("#variantLabel").value, value: $item("#variantId").value }) }); $w('#variantsList').data = data; const toInsert = { title: $w('#poemTitle').value, price: $w('#poemPrice').value, description: $w('#poemDescription').value, mainProductId: $w('#poemId').value, variants: $w('#variantsList').data } try { await wixData.insert("", toInsert); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Return to Poems Manager }); } catch(error) { console.error(error); } ``` **Lines 1-10**. Get the values from each item in the `#variantsList` repeater. **Lines 11-17**. Create a new object containing the values of all the input fields, including all the variants. **Line 18**. Using the `wix-data` package we imported at the top of code, [insert](https://dev.wix.com/docs/sdk/backend-modules/data/items/insert.md) the new poem data into the **Poems** collection. This completes the code for the **Add poem** page. Here’s the full code: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; $w.onReady(function () { let variantsArray = []; $w('#variantsList').hide(); $w("#variantsList").onItemReady(($item, itemData, index) => { $item("#variantId").value = itemData.value; $item("#variantLabel").value = itemData.label; $item('#deleteVariantButton').onClick( () => { variantsArray.splice(itemData.realIndex,1) variantsArray.forEach((item, index) => { item.realIndex = index; }); $w('#variantsList').data = variantsArray; }) }); $w('#addVariantButton').onClick( () => { if ($w('#variantsList').hidden) { $w('#variantsList').show(); } let highestId = 0; variantsArray.forEach((item, index) => { if(Number(item._id) >= highestId) highestId = Number(item._id) + 1; }); const newItem = { _id: highestId.toString(), value: '', label: '', realIndex: variantsArray.length }; variantsArray.push(newItem); $w('#variantsList').data = variantsArray; }); $w("#savePoemButton").onClick(() => { const data = []; $w('#variantsList').forEachItem(($item, itemData, index) => { data.push({ _id: index.toString(), label: $item("#variantLabel").value, value: $item("#variantId").value }) }); $w('#variantsList').data = data; const toInsert = { title: $w('#poemTitle').value, price: $w('#poemPrice').value, description: $w('#poemDescription').value, mainProductId: $w('#poemId').value, variants: $w('#variantsList').data } try { await wixData.insert("", toInsert); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Return to Poems Manager }); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }); } catch(error) { console.error(error); } }); $w('#cancelButton').onClick( ()=> { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }) }) }); ``` Next, we'll write the code for **Poems Manager**. 1. Return to the **Poems Manager** page in the dashboard interface and open the code panel. We’ll start with the import statements we added on the other page: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; ``` 1. Recall that our **Poems Manager** page has 2 states: empty and nonempty. Whenever the page loads, it needs to determine which state to display. To determine the state, we simply query the **Poems** collection to check if there are items to display. Below the import statements, add the following function: ```js async function determineManagerState() { try { const result = await wixData .query("") .find(); if (result.items.length > 0) { $w('#multiStateBox1').changeState("poemsList"); } else { $w('#multiStateBox1').changeState("emptyState"); } } catch(error) { console.log(error); } } ``` `determineManagerState()` queries the collection to see if there are poems, and based on the length of the results sets the current state of the multi-state box. Beneath `determineManagerState()`, we add an `onReady()` handler: ```js $w.onReady(async function () { determineManagerState(); $w('#listOfPoems').onItemRemoved((itemData) => { determineManagerState(); }) $w('#mainAddPoemButton').onClick( () => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) $w("#addFirstPoemButton").onClick(() => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) }); ``` **Line 2**. Determine the correct state when the page first loads. **Line 4-6**. Determine the correct state after an item has been removed from the repeater that lists the poems. This handles the edge case of a user deleting the last poem in the list. **Lines 8-18**. When a user clicks any button to add a new poem–whether in the empty state or not–navigate to the **Add poem** page. The page ID can be located in the dashboard interface section under **More Actions**. Here’s the full code for the **Poems Manager** page: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; async function determineManagerState() { try { const result = await wixData .query("") .find(); if (result.items.length > 0) { $w('#multiStateBox1').changeState("poemsList"); } else { $w('#multiStateBox1').changeState("emptyState"); } } catch(error) { console.log(error); } } $w.onReady(async function () { determineManagerState(); $w('#listOfPoems').onItemRemoved((itemData) => { determineManagerState(); }) $w('#mainAddPoemButton').onClick( () => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) $w("#addFirstPoemButton").onClick(() => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) }); ``` Our app now has 2 functioning dashboard pages, along with a working item page. You can preview some of the features in Blocks. We’ll test everything in the last step of this tutorial, but first, we need to complete the final part of our app and implement the Get Catalog service plugin. **Next up:** [Step 5 | Implement a Self-managed Catalog](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 5 | Implement a Self-hosted Catalog Service Plugin ## Article: Step 5 | Implement a Self-hosted Catalog Service Plugin ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md ## Article Content: # Step 5 | Implement a Self-Managed Catalog Service Plugin > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md) In [Step 2](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md), we created a catalog database to store the poems a Wix user sells on their site. In order to connect that catalog to the eCommerce platform, we need to create a server to implement the [Catalog service plugin](https://dev.wix.com/docs/rest/business-solutions/e-commerce/service-plugins/catalog-service-plugin/introduction.md). The service plugin includes a single method called [Get Catalog Items](https://dev.wix.com/docs/rest/business-solutions/e-commerce/service-plugins/catalog-service-plugin/get-catalog-items.md). Wix calls this method when a cart or checkout on the Wix user’s site is updated. In the call, it includes the IDs of any items in the cart, and in return it expects information from the catalog about each item. Wix doesn’t know where the catalog database is located–it only knows the location of the service plugin endpoint. Therefore, the service plugin must retrieve the items from the catalog database and return them to Wix in the correct format. In our case, this means our service plugin needs to communicate with the Wix site to access the catalog database and return the required item details. In this example app, we’ve placed the catalog database on a Wix site. But remember that in your own business solution, you can choose to locate the catalog database anywhere, including on your server or in an external database. You just need to change your server logic to fetch from that location. There are several things our server code must do to fulfill a Get Catalog Items request: 1. Verify and decode the incoming request sent as a JSON web token. 2. Extract the app instance ID and requested item IDs from the decoded payload. 3. Create a client to communicate with the Wix site that sent the request. 4. Use the client to request information from the site catalog database about each item. 5. Return all item information in a form that matches the Get Catalog Items response object. We’ll write our server code in [Express](https://expressjs.com/), so the first thing we need to do is set up Express and other necessary packages. 1. Install the following `npm` packages from Wix: ```bash npm install @wix/sdk npm install @wix/data ``` 1. [Install Express](https://expressjs.com/en/) and set up an app. Use version 4.17.0. You can choose to use `express-generator`, but it isn’t necessary for this example. 1. Once your Express app is created, open the `app.js` file and paste the following code: ```js const express = require('express'); const jose = require('jose'); const { createClient, AppStrategy } = require('@wix/sdk'); const { collections, items } = require("@wix/data"); const app = express(); const port = 3000; app.use(express.text()); async function verify(jwt) {} app.post('/get-catalog-items', async (request, response) => {}) app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) module.exports = app; ``` Most of the above code is standard Express. However, we’ve added some extra lines that are specific to our business solution: **Lines 2-4**. Import 3 libraries that we’ll need in our server code: - Jose for JWT verification. - Wix SDK to create a Wix client. - Wix Data to work with collection data on a site. **Line 8**. Set up the [express.text()](https://expressjs.com/en/4x/api.html#express.text) middleware function. This function parses the incoming request, including the encoded body, into a string that our verification function can handle. **Line 10**. Create our `verify()` function, which will accept a JWT token as a string and decode it. We’ll write the code for this in a moment. **Line 12**. A POST method that receives Get Catalog Items requests from Wix and returns the correct information from the catalog. We’ll write the code for this shortly. Let’s start by writing the code for the `verify()` function. ## Verify the JWT token As with all [service plugins at Wix](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/backend-extensions/add-self-hosted-service-plugin-extensions-with-rest.md), when a Wix site calls Get Catalog Items, it encodes the request body as a JWT. Therefore, in the service plugin we must decode the token in order to extract information like the app instance and catalog IDs. In this tutorial, we use the [jose](https://github.com/panva/jose) library for JWT verification. 1. In the `verify()` function we created before, add a `try`-`catch` block. Above the `try` block, add 2 `const` variables `alg` and `spki`. The first variable defines the RSA algorithm we’re using. In the case of requests from Wix, this is RS256, so we’ll assign the variable this value as a string: ```javascript const alg = 'RS256'; ``` The second variable, `spki`, is our public key. Each app in the Custom Apps list has a unique public key. To find this key, return to the **App ID & keys** modal in the [app dashboard](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome). Copy the public key from the modal. ![Copy the public key from the App ID & keys modal in your app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a3835faeae944997fab2a4e09b30b720.png) Paste the public key as the value for `spki`. 1. Let’s start adding code to the `try` block. We first add some error checking code that ensures the `jwt` parameter is a string: ```js try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } } catch (error) {} ``` Next, we add the functions that actually do the decoding. The first function we add is [importSPKI()](https://github.com/panva/jose/blob/main/docs/key/import/functions/importSPKI.md): ```javascript const publicKey = await jose.importSPKI(spki, alg); ``` We pass the `spki` and `alg` variables to `importSPKI()` and store the result in a new variable called `publicKey`. We then pass this result to the [`jwtVerify()`](https://github.com/panva/jose/blob/main/docs/jwt/verify/functions/jwtVerify.md) function, along with the `jwt` parameter and an [JWTVerifyOptions](https://github.com/panva/jose/blob/main/docs/jwt/verify/interfaces/JWTVerifyOptions.md) object: ```javascript const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, {       issuer: 'wix.com',       audience: '',       maxTokenAge: 60,       clockTolerance: 60     }); ``` Set the attributes in the `options` parameter as shown. These options allow us to [validate](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/backend-extensions/add-self-hosted-service-plugin-extensions-with-rest.md#validating-request-signatures) requests received from Wix. To set the `audience` value, copy your app ID from the app dashboard. Finally, we return the payload. ```js return payload; ``` 1. Now we’ll add code to the `catch` block. You can add more complex error handling later, but for now we’ll simply throw an error with a message that the verification failed. ```javascript catch (error) {     throw new Error('JWT verification failed');   } ``` The full code for the function should now look like this: ```js async function verify(jwt) { const alg = 'RS256' const spki = ``; try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } const publicKey = await jose.importSPKI(spki, alg) const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'wix.com', audience: '', maxTokenAge: 60, clockTolerance: 60 }) return payload; } catch (error) { throw new Error('JWT verification failed'); } } ``` ## Implement Get Catalog Items Now that we’ve set up our `verify()` function, we can implement the [Get Catalog Items](https://dev.wix.com/docs/rest/business-solutions/e-commerce/service-plugins/catalog-service-plugin/get-catalog-items.md) endpoint, so that Wix can make calls to our server to get information about catalog products. 1. Go to the POST method we created below the `verify()` function: ```javascript app.post('/get-catalog-items', async (reqest, response) => {}) ``` Note a couple of important things here: - The path we route catalog requests to must be in snakecase. Make sure your  path is identical to the one shown above. - We will need to make asynchronous calls inside our handler function, so mark it as `async`. 1. Inside the handler function add a `try`-`catch` block. In the `try` block add the following lines of code: ```js const token = request.body; const body = await verify(token); const instanceId = body.data.metadata.instanceId; const requestedItems = body.data.request.catalogReferences; ``` Let’s break down what this code is doing: **Line 1.** Isolate the request body, which is in JWT format, and store it in a variable `token`. **Line 2**. Pass the token to `verify()`. If the token is valid, `verify()` returns the request body as a decoded object, which we store in a JS object called `body`. **Lines 3-4**. Parse `body` to extract 2 important pieces of information: - The instance ID. This tells us which instance of our app made the request. We’ll use this shortly to query the site catalog. - The list of items that the Wix site is requesting information about. This tells us which items to retrieve information about from the catalog. 1. Because our server is self-managed, we need to [create a client](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-the-wix-client.md) in order to make calls to a Wix site. We use the Wix SDK [createClient()](https://dev.wix.com/docs/sdk/core-modules/sdk/wix-client.md#createclient) function to do this. `createClient()` expects a configuration object with at least 2 attributes: - `modules`: The Wix SDK modules your self-managed app uses. This typically matches the modules that you imported. - Your app’s [authentication strategy](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-authentication.md). In our example the app will make API calls, so we use the [`AppStrategy`](https://dev.wix.com/docs/sdk/core-modules/sdk/app-strategy.md) authentication strategy. Add the following code to the try block: ```js const wixClient = createClient({ modules: { items }, auth: AppStrategy({ appId: "", appSecret: "", publicKey: `` instanceId: instanceId, }), }); ``` **Line 2**. Define the modules we use in this client. In this case, we only use the [items](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md) module. **Lines 3-8**. Define the `AppStrategy` object. This object requires your app ID, app secret, and public key from the [app dashboard](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome). The `AppStrategy` object also requires the ID of the app instance. This tells the app which site it’s communicating with. We take the ID we parsed from the decoded request body and pass it to the `AppStrategy` constructor. This sets up our client and prepares us to make calls to the site collection. 1. Before adding the next piece of code, we need the ID of the **Poems** collection we created in Step 2. Return to the Blocks app and go to your collection in the CMS. Click **Edit Settings** to open the collection settings. ![Go to CMS settings in Blocks to get your collection ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/26a056eb002090892cd4a3f75c87d478.png) Beneath the collection name, copy the collection ID. 1. Return to the Express code. Beneath the createClient() method we added, add this code: ```js const catalogItems = await Promise.all(requestedItems.map(async (reference) => { const results = await wixClient.items .query("") .eq("mainProductId", reference.catalogReference.catalogItemId) .find(); const item = results.items[0]; const options = (reference.catalogReference.options !== null) ? reference.catalogReference.options : {}; return { "catalogReference": { "appId": "", "catalogItemId": item.mainProductId, "options": options }, "data": { "productName": { "original": item.title }, "itemType": { "preset": "SERVICE" }, "price": item.price, "priceDescription": { "original": "Number of lines" } } }; })); response.send({catalogItems}); ``` Before we review this code line by line, it’s important to understand its overall purpose. The code takes in the `requestedItems` array we created containing catalog references for the requested items, and returns a new array of catalog items in the required format. To do so, it performs the following steps: 1. Extracts the catalog ID of each item in the `requestedItems` array. 2. Queries the site catalog for that ID. 3. Organizes the returned information from the site into an object that matches the Get Catalog Items response. Let’s break down the code line by line: **Line 1**. Wrap the Javascript `map()` function in a `Promise.all()` statement that returns the array of objects only after the query has been resolved for all items. **Lines 3-6**. Using the Wix client we created, [query](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md) the catalog database on the site. Paste your collection ID as the parameter. **Line 7**. Store the query result. **Line 8**. Extract the variant information from the requested item and place it in an object `options` that can be passed to the returned item. **Lines 10-28**. Set up the response object in the correct format. **Line 31**. Returns the complete array of catalog references `catalogItems` to the caller. 1. To complete the Get Catalog Items endpoint, we’ll fill out the `catch` block. As with `verify()`, you can add more complex error handling later, but for now we’ll just return a 400 status with an error message: ```javascript catch (error) {     response.status(400).send({ error: error.message });   } ``` 1. Our server code is complete, but we need to complete one extra step to make sure it can communicate with the product catalog on the site. In our code, we call the [`query()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md) method of the Wix Data `items` module. Like all API methods at Wix, in order for our app to call this method, it must have the correct [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md). We can locate these permissions in the reference above the method declaration: ![Location of API method permissions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c57e470ce368f5899f2acd26e0b56e45.png) The specific permission we need to call `query()` is **READ DATA ITEMS**. To add this permission to our app, return to the app dashboard and go to [permissions](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fdev-center-permissions). Click **+ Add Permissions**. The easiest way to locate the permission you need to add is to search by name or ID. Search for “read data items”. Select the correct permission and click **Save** to add it to your app. ![Add read data items permissions to your app](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8bce6c9441adf55786718d32d50ff754.png) The permission should now appear in the permissions list: ![Permissions list app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/196ee15e0d1481346b419878aea7714c.png) Now our app can communicate with the product catalog on a site. Our service plugin code is now complete. Here’s the full code for `app.js`: ```js const express = require('express'); const jose = require('jose'); const { createClient, AppStrategy } = require('@wix/sdk'); const { collections, items } = require("@wix/data"); const app = express(); const port = 3000; app.use(express.json()); app.use(express.text()); async function verify(jwt) { const alg = 'RS256' const spki = ``; try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } const publicKey = await jose.importSPKI(spki, alg) const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'wix.com', audience: '', maxTokenAge: 60, clockTolerance: 60 }) return payload; } catch (error) { throw new Error('JWT verification failed'); } } app.post('/get-catalog-items', async (request, response) => { try { const token = request.body; const body = await verify(token); const instanceId = body.data.metadata.instanceId; const requestedItems = body.data.request.catalogReferences; const wixClient = createClient({ modules: { collections, items }, auth: AppStrategy({ appId: "", appSecret: "", publicKey: ``, instanceId: instanceId, }), }); const catalogItems = await Promise.all(requestedItems.map(async (reference) => { const results = await wixClient.items .query("") .eq("mainProductId", reference.catalogReference.catalogItemId) .find(); const item = results.items[0]; const options = (reference.catalogReference.options !== null) ? reference.catalogReference.options : {}; return { "catalogReference": { "appId": "", "catalogItemId": item.mainProductId, "options": options }, "data": { "productName": { "original": item.title }, "itemType": { "preset": "SERVICE" }, "price": item.price, "priceDescription": { "original": "Number of lines" } } }; })); response.send({catalogItems}); } catch (error) { response.status(400).send({ error: error.message }); } }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) module.exports = app; ``` Deploy your code on your chosen server. ## Create an Ecom Catalog in the app dashboard Once we deploy our server code, we need to tell Wix where to send its requests. To do this, we’ll return to our app dashboard and create an Ecom Catalog extension: 1. In the app dashboard, go to the [extensions](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fextensions) page. 1. Click **+ Create Extension** and search for **Ecom Catalog**. Click **+ Create**. 1. In the JSON editor, add a key `deploymentUri`. As its value, paste the deployment URI of your server. For example: ```javascript {   "deploymentUri": "https://the-poems-manager.com" } ``` 1. Click **Save**. The **Ecom Catalog** extension now appears on our app’s extension list: ![Extensions list app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb61042bfd4d2c6585761417f220d8bd.png) Our app is now complete and able to function on a site. In the last article, we'll build the app, install it on a site, and test the functionality we created throughout this tutorial. **Next up:** [Step 6 | Test your Business Solution on a Site](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 6 | Test your Business Solution on a Site ## Article: Step 6 | Test your Business Solution on a Site ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md ## Article Content: # Step 6 | Test your Business Solution on a Site > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md) In this article, we'll test the [poem-selling app](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/overview.md) that we spent the past 5 articles building. In order to test the app, first make sure your server code is deployed and the server is live. Then, we need to build the app in Blocks: 1. If you haven’t already, [create a Wix site](http://wixstudio.new). 1. In the top right corner of the Blocks editor, click **Release**, then click **Release** again in the modal that opens. ![Release a build](https://wixmp-833713b177cebf373f611808.wixmp.com/images/384c8469ea58af08ec2a7f96011adcca.png) 1. Wait for your build to complete. A notification will appear on screen when the build is complete. Click **Select a Site** to open the site selector. ![Select a site to install your app on](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4d0824ae5e35fb1906d5cc8aa820ff76.png) 1. Select the site you want to test on. This opens the site editor. When the site loads, click **Install App**. ![Install the app on the site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0fcadb55395a762c2588836902356a08.png) Wait while Wix adds the app to your site. 1. Once the app is installed, the site editor will display the **App Widgets** section of **Add Elements**. Select the **Item selector** widget we created in Blocks and add it to the site page. 1. Let’s check that all the components we added in Blocks are here. In the left menu, go to **CMS** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/82123f77f0c38ce1ce3702a8fc82d820.png) > **Your Collections**. You’ll see the **Poems** collection we created in Blocks. If you open the collection, you’ll see that the items we created in Blocks still appear in the collection. This means that users who install your widget have default values that they can leave or edit. 1. Now let’s look at the dashboard. Close the CMS and in the top left corner open the menu and click **Dashboard**. ![Go to the site dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/31574c68c95f8ddcc59048aecb5102f7.png) 1. In the dashboard menu, hover over **Apps**. You’ll see one of the dashboard pages we created, **Poems Manager**. Note that the **Add a poem** dashboard page doesn’t appear here, because we hid it. ![Locate the Poems Manager dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/156c4e902a78e317a518db8614be9f30.png) Click **Poems Manager** to go to the dashboard page. Because our collection contains items, it displays the list of poems: ![Current Poems Manager dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/91f0280eb0253a663b4fd5cf8c92a9e9.png) 1. Let’s remove the current poems so that we can make sure our app recognizes the empty state. Click the icon button to remove each repeater item. We now see the empty state that we created: ![Display the empty state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5d6e3c5ffcd150ddb34a9ec6440c5151.png) 1. Next let’s add some poems to make sure the data functionality is working. Try clicking the **+ Add Poem** button inside the card. When we click the button, the site navigates us to the **Add a poem** dashboard page. ![Add a Poem dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b4ff50c059e3b8f5dc4e55a5ed646b3c.png) Click **Cancel** and make sure the site navigates you back to **Poems Manager** in its empty state. Then, you can try out the **Add a poem** button in the top right corner to return to **Add a poem**. Enter some poem details and add and remove variants. When you’re done, click **Save**. The **Poems Manager** should now be in the nonempty state and display the poem you added: ![Poems Manager updated with new poem](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ad394bdd8f8b69197d71a311016263fc.png) Add a couple more poems and then return to the site editor and open the CMS. Make sure the poems you entered all appear in the CMS. 1. Now let’s test the item page. Exit out of the CMS and in the top right corner of the site editor click **Publish**. The site notifies you when publishing is complete. Click on **View Site** next to the URL to open the live site. ![Publish and view your test site](https://wixmp-833713b177cebf373f611808.wixmp.com/images/9ff701c56b9d650073e796dafa392f07.png) 1. Go to the site page where you added your widget and test the widget out to make sure it works properly. You should initially see just the first dropdown which displays the different poem titles in your **Poems** collection. Once you make a selection, the other 2 elements should appear: ![App widget in action](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4c6543c5fbf4b148e8a3ee514541a71f.gif) 1. Now we’ll test the call to the server. When we click Add to cart, Wix calls the Get Catalog Items method that we set up in our server code, because we’re updating the current cart. Click **Add to cart**. If we’ve done everything correctly, you should see the cart page with the poem we added, it’s price updated to reflect the requested number of lines: ![Cart page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/67bd3228960dd6bbd5dcdcdac96a01af.png) 1. Test out the cart page by adjusting the number of lines, and make sure that the order price updates. Congratulations! You’ve built a business solution and successfully integrated it with Wix eCommerce. You can continue building your own business solution based on this example, or apply what you learned in this tutorial to an entirely new app. ## See also - [What is the Wix eCommerce Platform](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/overview/what-is-the-wix-e-commerce-platform.md) - [Wix App Developers: Build a New eCommerce Business Solution](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/get-started/apps/build-a-business-solution.md) - [Develop a Business Solution Item Page with Blocks](https://dev.wix.com/docs/rest/business-solutions/e-commerce/wix-e-commerce-platform-handbook/get-started/apps/develop-a-business-solution-item-page-with-blocks.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Wix eCom business solution ## Article: Wix App Developers: Build a Wix eCom business solution ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/common-use-cases/build-a-wix-e-com-business-solution.md ## Article Content: # Wix App Developers: Build a New eCommerce Business Solution If you are developing a full-scale business solution that requires eCommerce capabilities (similar to Wix Stores, Wix Bookings, or Wix Restaurants Orders), you can integrate your app with the Wix eCommerce platform and take advantage of its features. To enable this integration, you need to: 1. [Set up automatic installation of the eCommerce app.](#step-1--set-up-automatic-installation-of-the-ecommerce-app) 1. [Integrate an external catalog with your app.](#step-2--integrate-an-external-catalog-with-your-app) 1. [Integrate with the Inventory service plugin.](#step-3--integrate-with-the-inventory-service-plugin) 1. [Integrate with additional eCommerce services.](#step-4-optional--integrate-with-additional-ecommerce-services) (Optional) For a comprehensive guide, see our [tutorial on building a business solution integrated with the Wix eCommerce platform](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/overview.md). ## Step 1 | Set up automatic installation of the eCommerce app The eCommerce platform’s functionality can be added to a Wix site by installing the Wix Checkout & Orders app. However, the Wix Checkout & Orders app isn’t a standalone app that a site owner can install independently. To integrate eCommerce capabilities into your own business solution app, you need to make sure that your app automatically installs the Wix Checkout & Orders app as a dependency. Follow these steps to make your app install Wix eCommerce capabilities every time it's installed on a site: 1. Go to your [app's dashboard](https://dev.wix.com/apps/). 2. In the left sidebar, click **App Profile**. 3. In the sidebar submenu, click **App Audience**. 4. Under **Required Wix Products**, select **Yes, additional products are required**. 5. In the **Choose products** list, select **Wix Checkout & Orders**. ![Wix Checkout & Orders](https://wixmp-833713b177cebf373f611808.wixmp.com/images/444cfe17d5680683847c9ffc3870665c.png) 6. Click **Save**. You have now configured your app to frontload the Wix eCommerce platform. Whenever your app is installed on a site, it now automatically installs the Wix Checkout & Orders app which adds standard Wix eCommerce pages and components to the site it’s installed on. After installing your app, a Wix site will have these features: **Site pages:** + Cart Page + Checkout Page (not visible in the editor) + Thank You Page **Dashboard pages:** + Orders + Gift Cards ## Step 2 | Integrate an external catalog with your app Any business solution app that integrates with the Wix eCommerce platform needs to connect with an external [catalog](invalid-url) containing available products or services. To get your app set up with an external catalog, follow these steps: 1. Implement the Catalog service plugin to make your external catalog's data available to Wix in the required format and structure. Refer to the [Catalog service plugin documentation](https://dev.wix.com/docs/rest/business-solutions/e-commerce/introduction.md) for detailed instructions. 1. Create an [item page](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/get-started/apps/develop-a-business-solution-item-page-with-blocks.md) (a product page, a service page, or equivalent) and/or a gallery page for your app that displays information about individual items for sale. This page should include functionality for adding items to the site visitor's cart, using the [Cart API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart.md). For guidance on creating a page for your app, consult [About Site Page Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md). 1. (Optional) To enable Wix users to manage catalog items from their site dashboard, develop a [dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) for your app. For detailed instructions on developing Wix apps, consult our comprehensive documentation on [building Wix apps](https://dev.wix.com/docs/build-apps.md). ## Step 3 | Integrate with the Inventory service plugin Any business solution app that integrates with the Wix eCommerce platform and manages inventory needs to prevent orders for unavailable items and keep stock levels in sync with order flows. To get your app set up with inventory management, follow these steps: 1. Implement the Inventory service plugin to integrate with Wix's unified inventory system. This enables your app to automatically decrement availability when items are ordered or paid for, and increment it when orders are canceled or refunded. Refer to the [Inventory service plugin documentation](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/inventory-service-plugin/introduction.md) for detailed instructions. 1. (Optional) Configure multi-location inventory management if your business solution involves tracking stock across multiple physical or virtual locations. The Inventory service plugin is especially useful for business solutions involving physical products, digital goods with limited availability, or time-based services like appointments or event tickets. ## Step 4 (Optional) | Integrate with additional eCommerce services You can further customize your app's functionality by integrating with additional services available on the eCommerce platform. The [Tax Groups service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/tax/tax-groups-integration-service-plugin/introduction.md) enables you to create and manage custom tax groups to categorize items from your app's catalog based on distinct tax treatments. This integration is required to charge tax on your external catalog items. ## See also + [Tutorial | Build an eCommerce Business Solution](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/overview.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Wix eCom feature app ## Article: Wix App Developers: Build a Wix eCom feature app ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/common-use-cases/build-a-wix-e-com-feature-app.md ## Article Content: # Wix App Developers: Build a Feature App That Extends eCommerce Functionality Many apps in the Wix App market enhance the eCommerce functionality of sites that already integrate with the Wix eCommerce platform. If you are creating such an app, your app should require that Wix users first install a business solution app that integrates the Wix eCommerce platform, such as Wix Bookings or Wix Stores. To prevent your app from being installed on sites that don't have an eCommerce business solution app already installed, follow these steps: 1. Go to your [app's dashboard](https://dev.wix.com/apps/). 2. In the left sidebar, click **App Profile**. 3. In the sidebar submenu, click **App Audience**. 4. Under **Required Wix Products**, select **Yes, additional products are required**. 5. In the **Choose products** list, select the required business solutions, such as **Wix Stores**, **Wix Bookings**, and **Wix Restaurants Orders**. If you select more than 1 app, your app will require at least 1 of the selected apps. 6. Click **Save**. To check whether a business solution is installed on a site, you can also use the [Get App Instance](https://dev.wix.com/docs/api-reference/app-management/app-instance/get-app-instance.md) method. The response object contains a list of installed Wix apps in `site.installedWixApps`. To learn more about developing a Wix app that extends eCommerce functionality, read about [integrating with Wix business solutions](https://dev.wix.com/docs/build-apps/get-started/overview/integrating-with-wix-s-business-solutions.md). If you are new to developing apps for installation on Wix sites, learn more about the [app-building journey](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-apps.md), or get started from one of our growing selection of [app templates](https://dev.wix.com/docs/build-apps/get-started/templates/get-started-from-an-app-template.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Extensions and APIs for Dropshipping Apps ## Article: Extensions and APIs for Dropshipping Apps ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/common-use-cases/extensions-and-apis-for-dropshipping-apps.md ## Article Content: # Extensions and APIs for Dropshipping Apps Dropshipping apps allow Wix users to sell products without maintaining inventory. The app connects a Wix site to an external supplier who handles product sourcing and fulfillment. This article lists the APIs, events, and service plugins you need to use to build a dropshipping app. When developing your app, make sure to create a [dashboard extension](#extensions) to provide Wix users with an interface to connect their supplier account, browse products to import, and manage orders. ## Permissions Your app needs the following permission scopes: - **Manage Products** (`SCOPE.DC-STORES.MANAGE-PRODUCTS`): Required for creating and updating products. - **Manage Orders** (`SCOPE.DC-STORES.MANAGE-ORDERS`): Required for reading orders and creating fulfillments. ## APIs and events Use these APIs, events, and service plugins to build your dropshipping integration. > **Note:** This reference is intended as a helpful starting point. Your app may require additional resources. Explore the full [API Reference](https://dev.wix.com/docs/api-reference.md) for more options. ### Products Import products from your supplier into the Wix Stores catalog. - [Create Product](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/create-product.md) / [Bulk Create Products](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/bulk-create-products.md): Add new products to the Wix Stores catalog. - [Update Product](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/update-product.md) / [Bulk Update Products](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/bulk-update-products.md): Update product details such as price, stock, or description. - [Query Products](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/query-products.md): Retrieve products from the catalog. ### Product restrictions Restrict Wix users from editing specific product fields, such as price or inventory, to keep product data consistent with your supplier. - [Product Restrictions service plugin](https://dev.wix.com/docs/api-reference/business-solutions/stores/service-plugins/product-restrictions-v3/introduction.md): Define which product fields Wix users can edit for products your app manages. ### Orders Receive and process orders to forward to your supplier. - [Order Approved event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-approved.md): Triggers when an order is ready for fulfillment. - [Order Paid event](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/order-paid.md): Triggers when payment is confirmed. - [Order Updated event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-updated.md): Triggers when order details change, such as shipping address updates. - [Get Order](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/orders/get-order.md): Retrieve order details to send to your supplier. ### Fulfillment Create and manage order fulfillments to track which items have been shipped and their delivery status. - [Create Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/create-fulfillment.md) / [Bulk Create Fulfillments](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/bulk-create-fulfillments.md): Creates an order fulfillment. - [Update Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/update-fulfillment.md): Update a fulfillment's tracking info or status as it progresses through delivery. - [Delete Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/delete-fulfillment.md): Deletes an existing order fulfillment. - [List Fulfillments For Single Order](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/list-fulfillments-for-single-order.md): Retrieve all fulfillments for an order to check which items have already been shipped. ## Extensions Extensions add functionality to your app and integrate it with the Wix platform. ### Dashboard extension (required) [Dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) add customizable features like a page or plugin to the Wix Dashboard. Use a dashboard extension to let Wix users connect their supplier account, browse products to import, and manage orders. ### Checkout extensions (optional) Extend the checkout experience with your supplier's data. - [Shipping Rates service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/shipping-rates/introduction.md): Provide custom shipping rates at checkout. - [Validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md): Check stock availability or shipping restrictions before purchase. - [Additional Fees service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/additional-fees/additional-fees-service-plugin/introduction.md): Add handling charges, customs duties, or insurance. ## See also - [About Launching Apps](https://dev.wix.com/docs/build-apps/launch-your-app/about-launching-apps.md) - [Stores Catalog API](https://dev.wix.com/docs/rest/business-solutions/stores/catalog-v3/products-v3/introduction.md) - [eCommerce Orders API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/introduction.md) - [Order Fulfillments API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Extensions and APIs for Fulfillment Apps ## Article: Extensions and APIs for Fulfillment Apps ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/common-use-cases/extensions-and-apis-for-fulfillment-apps.md ## Article Content: # Extensions and APIs for Fulfillment Apps Fulfillment apps help Wix users manage the process of preparing and delivering orders to customers. These apps typically integrate with shipping carriers, warehouses, or 3rd-party logistics providers. This article lists the APIs and events you need to use to build a fulfillment app. When developing your app, make sure to create a [dashboard extension](#extensions) to provide Wix users with an interface to connect their shipping accounts, configure fulfillment settings, and manage shipments. ## Permissions Your app needs the following permission scopes: - **Read Orders** (`SCOPE.DC-STORES.READ-ORDERS`): Required for retrieving order details. - **Manage Orders** (`SCOPE.DC-STORES.MANAGE-ORDERS`): Required for creating and updating fulfillments. - **Manage Products** (`SCOPE.DC-STORES.MANAGE-PRODUCTS`): Required for updating product inventory. ## APIs and events Use these APIs and events to build your fulfillment integration. > **Note:** This reference is intended as a helpful starting point. Your app may require additional resources. Explore the full [API Reference](https://dev.wix.com/docs/api-reference.md) for more options. ### Orders Receive and retrieve orders that need fulfillment. - [Order Approved event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-approved.md): Triggers when an order is ready for fulfillment. - [Order Paid event](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/order-paid.md): Triggers when payment is confirmed. - [Order Updated event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-updated.md): Triggers when order details change. - [Get Order](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/orders/get-order.md): Retrieve order details. ### Fulfillment Create and manage order fulfillments to track which items have been shipped and their delivery status. - [Create Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/create-fulfillment.md) / [Bulk Create Fulfillments](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/bulk-create-fulfillments.md): Creates an order fulfillment. - [Update Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/update-fulfillment.md): Updates an existing order fulfillment. - [Delete Fulfillment](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/delete-fulfillment.md): Deletes an existing order fulfillment. - [List Fulfillments For Single Order](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/list-fulfillments-for-single-order.md): Retrieves fulfillments for an order. ### Fulfillment events Listen for changes to fulfillments. - [Order With Fulfillments Updated event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/order-with-fulfillments-updated.md): Triggers when an order's fulfillments are created, updated, or deleted. ## Extensions Extensions add functionality to your app and integrate it with the Wix platform. ### Dashboard extension (required) [Dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) add customizable features like a page or plugin to the Wix Dashboard. Use a dashboard extension to let Wix users connect their shipping accounts, configure fulfillment settings, and manage shipments. ### Checkout extensions (optional) Extend the checkout experience with your shipping data. - [Shipping Rates service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/shipping-rates/introduction.md): Provide custom shipping rates at checkout. - [Validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md): Validate carts and checkouts with custom logic. - [Additional Fees service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/additional-fees/additional-fees-service-plugin/introduction.md): Add custom fees to carts and checkouts. ## See also - [About Launching Apps](https://dev.wix.com/docs/build-apps/launch-your-app/about-launching-apps.md) - [Order Fulfillments API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/introduction.md) - [Order Fulfillments Sample Flow](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-fulfillments/sample-flow.md) - [eCommerce Orders API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Extensions and APIs for Invoicing Apps ## Article: Extensions and APIs for Invoicing Apps ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/common-use-cases/extensions-and-apis-for-invoicing-apps.md ## Article Content: # Extensions and APIs for Invoicing Apps Invoicing apps generate invoices, receipts, and manage billing for orders placed on Wix sites. This article lists the APIs and events you need to use to build an invoicing app. When developing your app, make sure to create a [dashboard extension](#extensions) to provide Wix users with an interface to view invoices, configure invoice templates, and manage billing settings. ## Permissions Your app needs the following permission scopes: - **Read Orders** (`SCOPE.DC-STORES.READ-ORDERS`): Required for retrieving order details. - **Read Bookings - Including Participants** (`SCOPE.DC-BOOKINGS.READ-BOOKINGS-SENSITIVE`): Required for retrieving booking details. - **Read Payments** (`SCOPE.DC-CASHIER.READ-PAYMENTS`): Required for retrieving payment details. ## APIs and events Use these APIs and events to build your invoicing integration. > **Note:** This reference is intended as a helpful starting point. Your app may require additional resources. Explore the full [API Reference](https://dev.wix.com/docs/api-reference.md) for more options. ### Orders Receive order notifications and retrieve order details for invoicing. - [Order Approved event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-approved.md): Triggers when an order is approved. - [Order Paid event](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/order-paid.md): Triggers when payment is confirmed. - [Order Updated event](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/order-updated.md): Triggers when order details change. - [Get Order](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/orders/get-order.md): Retrieve order details. - [List Invoices For Multiple Orders](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-invoices/list-invoices-for-multiple-orders.md): Check if invoices exist for orders. ### Billing Handle payment authorization, capture, and refunds. - [Authorize Charge With Saved Payment Method](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/authorize-charge-with-saved-payment-method.md): Authorizes a charge for an order. - [Capture Authorized Payments](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/capture-authorized-payments.md): Captures previously authorized payments. - [Void Authorized Payments](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/void-authorized-payments.md): Voids previously authorized payments. - [Get Order Refundability](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/get-order-refundability.md) / [Calculate Refund](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/calculate-refund.md): Check refund eligibility and calculate amounts. - [Refund Payments](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-billing/refund-payments.md): Refunds order payments. - [Purchased Item Invoice Status Updated event](https://dev.wix.com/docs/rest/app-management/app-billing/billing/purchased-item-invoice-status-updated.md): Triggers when invoice status changes. ### Receipts Generate and manage receipts for completed transactions. - [Create Receipt](https://dev.wix.com/docs/rest/business-management/get-paid/receipts/receipts/create-receipt.md): Creates a receipt. - [Get Receipt](https://dev.wix.com/docs/rest/business-management/get-paid/receipts/receipts/get-receipt.md) / [Query Receipts](https://dev.wix.com/docs/rest/business-management/get-paid/receipts/receipts/query-receipts.md): Retrieve receipts. ### Billable items Manage custom products or services for invoicing. - [Create Billable Item](https://dev.wix.com/docs/rest/business-management/get-paid/billable-items/create-billable-item.md) / [Update Billable Item](https://dev.wix.com/docs/rest/business-management/get-paid/billable-items/update-billable-item.md): Create or update billable items. - [Query Billable Items](https://dev.wix.com/docs/rest/business-management/get-paid/billable-items/query-billable-items.md): Retrieve billable items. ## Extensions Extensions add functionality to your app and integrate it with the Wix platform. ### Dashboard extension (required) [Dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) add customizable features like a page or plugin to the Wix Dashboard. Use a dashboard extension to let Wix users view invoices, configure invoice templates, and manage billing settings. ## See also - [About Launching Apps](https://dev.wix.com/docs/build-apps/launch-your-app/about-launching-apps.md) - [eCommerce Orders API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/introduction.md) - [Order Billing API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/order-billing/introduction.md) - [Receipts API](https://dev.wix.com/docs/api-reference/business-management/get-paid/receipts/receipts/introduction.md) - [Billable Items API](https://dev.wix.com/docs/api-reference/business-management/get-paid/billable-items/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Get an Idea ## Article: Get an Idea ## Article Link: https://dev.wix.com/docs/build-apps/get-started/get-an-idea.md ## Article Content: # Get an Idea for Your Next App Are you searching for your next app idea? We've gathered the top feature requests from Wix users. These are high-demand features that can boost user adoption of your apps. After you build an app based on this list, let us know so we can help promote it. [](#open-feature-requests) ## Monetize your app When your app is ready, you can add it to the [Wix App Market](https://www.wix.com/app-market) to reach millions of Wix users worldwide—and sell your apps at scale. For more information, see [About Launching Apps](https://dev.wix.com/docs/build-apps/launch-your-app/about-launching-apps.md). ## Let us know you're supporting a feature If you're supporting one of these top feature requests, mention it when you [submit your app for review](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/about-app-distribution.md) or notify us through [WixBot](https://www.wix.com/support-chatbot?nodeId=25a57397-ccf7-4376-8b74-48d51edf7159&referral=devRels). In the message, be sure to include your app ID and app name. Apps that meet our quality standards will be promoted through internal tools, such as emails and notifications to account managers, helping users discover your feature and increasing adoption. **Ready to build?** [Get Started](https://manage.wix.com/account/custom-apps) ## Open feature requests Here's a list of highly requested features from our user community. ### eCommerce & Stores | **Feature** | **Description** | |---|---| | **Additional fees in Edit Order page (Ecomm, Stores, Bookings)**

Date Added: November 1, 2024 | **About the request:** Add fees for extra services like fragile packaging, shipping insurance, or gift wrapping at checkout. Providing these optional fees can boost customer satisfaction and generate new revenue streams for merchants by offering value-added services.

**Resources to help you get started:**
  • [Dashboard plugin on the Order page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/supported-wix-app-dashboard-pages/wix-e-commerce/wix-e-commerce-edit-order-page.md)
  • [Additional fees service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/additional-fees/additional-fees-service-plugin/introduction.md)
| | **Verifying Shipping Addresses (eCom/Stores)**

Date Added: December 14, 2025 | **About the request:** Systematically verifying the shipping location to ensure accurate delivery before processing the order.

**Resources to help you get started:**
  • [eCommerce validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md#about-the-wix-ecommerce-validations-service-plugin)
  • [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) to manage the areas which the verifier is working on
| | **Sticky 'Add to Cart' Button on the Product Page (Stores)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-stores-request-sticky-add-to-cart-button-on-the-product-page) | **About the request:** Enable a persistent 'Add to Cart' button that stays visible as shoppers scroll through product pages, keeping the purchase action always accessible without scrolling back up. This reduces friction in the buying journey and can increase conversion rates.

**Resources to help you get started:**
  • [Embedded script](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) on the product page
  • [Wix Stores product page plugin slots](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md)
| | **Creating Automatic Discounts for Subscription Products (Stores)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-stores-request-creating-automatic-discounts-for-subscription-products) | **About the request:** Allow merchants to create automatic discounts that apply specifically to subscription products, incentivizing customers to subscribe and boosting recurring revenue.

**Resources to help you get started:**
  • [Discount Rules API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/discount-rules/introduction.md)
  • [`subscriptionDetails` in the Product Catalog v3](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/product-object?apiView=SDK.md)
  • [About the Custom Discount Triggers Service Plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/custom-discount-triggers-integration-service-plugin/introduction.md)
| | **Integrating with the Australia Post Shipping Solution (Stores)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-stores-request-integrating-with-the-australia-post-shipping-solution) | **About the request:** Connect Wix Stores to Australia Post to offer real-time shipping rates and label generation for merchants shipping to and within Australia.

**Resources to help you get started:**
  • [Shipping rates service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/shipping-rates/shipping-rates-integration-service-plugin/introduction.md)
  • [Australia Post API](https://developers.auspost.com.au/) (external)
| | **Displaying a Price Range for Products with Options (Stores)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-stores-request-displaying-a-price-range) | **About the request:** Show a price range (e.g. "$20 – $50") for products that have variants with different prices, rather than displaying only the lowest price. This helps customers understand pricing options before selecting a variant.

**Resources to help you get started:**
  • [Site plugin on the product page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md)
  • [About the Wix Stores Catalog V3](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/introduction.md) to read variant prices
| ### Restaurants | **Feature** | **Description** | |---|---| | **Adding Multiple Pickup Locations (Restaurants)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-restaurants-request-adding-multiple-pickup-locations) | **About the request:** Let restaurants with more than one physical location offer multiple pickup points in their online orders flow, so customers can choose the most convenient location at checkout.

**Resources to help you get started:**
  • [eCommerce validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md) to intercept checkout and inject location selection
  • [Wix Locations API](https://dev.wix.com/docs/api-reference/business-management/locations/introduction.md)
  • [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) to map menu items to specific locations
| | **Charging Guests a Late Cancellation or No-Show Fee (Restaurants)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-restaurants-request-charging-guests-a-late-cancellation-or-no-show-fee) | **About the request:** Automatically charge a fee when a diner cancels too late or doesn't show up for their reservation, helping restaurants recover lost revenue and reduce no-shows.

**Resources to help you get started:**
  • [Wix Table Reservations API](https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/introduction.md)
  • [Wix Payments API](https://dev.wix.com/docs/api-reference/business-management/payments/introduction.md) to charge the held card
  • [Dashboard plugin on the Reservations page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/supported-wix-app-dashboard-pages/wix-restaurants/wix-restaurants-table-reservations-table-reservations-page.md) to configure the cancellation policy
| | **Adding Modifiers by Item Size (Restaurants)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-restaurants-request-adding-modifiers-by-item-size) | **About the request:** Allow restaurants to define different modifier options per item size — for example, adjusting topping quantities for a small vs. large pizza — making ordering more accurate and reducing kitchen errors.

**Resources to help you get started:**
  • [Wix Restaurants Menus API](https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/introduction.md)
  • [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) to manage conditional modifier rules
  • [Extended fields (schema plugin)](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md) to store modifier rules per size variant
| ### Bookings | **Feature** | **Description** | |---|---| | **Reputation management (Bookings)**

Date Added: December 14, 2025 | **About the request:** Prompt users to rank the business on external platforms like Google, Google Maps, Facebook, and Yelp after completing a meeting.

**Note:** Developers should bring their own existing solution to manage the reviews. You can trigger this flow by sending a post-meeting email to the client.

**Resources to help you get started:**
  • [About Wix Bookings Architecture](https://dev.wix.com/docs/api-reference/business-solutions/bookings/architecture.md)
  • [Bookings Reader V2: Sample Flows](https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-reader-v2/sample-flows.md#bookings-reader-v2-sample-flows)
  • [Query Attendance](https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/attendance/query-attendance.md)
  • [Query Extended Bookings](https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-reader-v2/query-extended-bookings.md)
| | **Migrating Customer Data from Another Platform to Wix (Bookings)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-bookings-request-migrating-customer-data-from-another-platform-to-wix) | **About the request:** Help business owners transfer existing customer records, booking history, and membership data from platforms like Acuity, Mindbody, or Calendly to Wix Bookings, eliminating manual data entry when switching platforms.

**Resources to help you get started:**
  • [Wix Bookings API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md)
  • [Contacts API](https://dev.wix.com/docs/api-reference/crm/members-contacts/contacts/contacts/introduction.md)
  • [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) for the import and field-mapping UI
| | **Sending Automated SMS Messages After Form Submission (Bookings)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-bookings-request-sending-automated-sms-messages-after-form-submission) | **About the request:** Automatically send an SMS from the merchant's own phone number to a lead immediately after they submit a booking inquiry form, helping businesses respond faster and increase lead conversion.

**Resources to help you get started:**
  • [Wix Automations API](https://dev.wix.com/docs/api-reference/business-management/automations/introduction.md) to trigger the SMS flow on form submission
  • [Wix Bookings API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md)
  • Integrate with an SMS provider (e.g. Twilio, Vonage) for message delivery
| | **Rescheduling Bookings with a Different Service (Bookings)**

Date Added: June 22, 2026

[Feature request ↗](https://support.wix.com/en/article/wix-bookings-request-rescheduling-bookings-with-a-different-service) | **About the request:** Allow customers or staff to reschedule a booking to a completely different service (not just a different time slot for the same service), giving clients more flexibility without having to cancel and rebook manually.

**Resources to help you get started:**
  • [Wix Bookings API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md)
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) or [About Dashboard Plugin Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/about-dashboard-plugin-extensions.md) to provide the rescheduling UI
| ## Resolved feature requests The following feature requests have been resolved by at least 1 app, or by Wix. | **Feature** | **Description** | |---|---| | **Share Google Drive content on a site**

Date Added: January 21, 2026 | **About the request:** Let visitors view and collaborate on Google Drive files and folders directly on your site, including Docs, Sheets, Slides, Forms, Microsoft Office files and PDFs.

**Resources to help you get started:**
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/create-a-settings-panel-for-a-site-widget-or-plugin-wix-cli-and-self-hosting.md)
| | **Interactive rotatable product image (Stores)**

Date Added: November 1, 2024 | **About the request:** Integrate rotatable 360-degree images on a Wix product page to enrich customer interaction. This shopping experience enhances understanding and boosts confidence in purchase decisions, potentially increasing sales.

**Resources to help you get started:**
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in product page
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=086a1107-3d84-43cc-992e-738055e01547&http_referrer=custom-apps-studio)
| | **Site Vacation Mode (eCom/Stores)**

Date Added: December 14, 2025 | **About the request:** A time-based logic that restricts access to the checkout of defined timeframes

**Resources to help you get started:**
  • An [embedded script](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) using [`openModal()`](https://dev.wix.com/docs/sdk/frontend-modules/window/open-modal.md) that executes on the checkout page, preventing purchases by displaying a custom "Store Closed" message during the defined timeframe, and preventing the user from completing the checkout
  • [Dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) to manage time of the vacation mode and messaging
| | **Free shipping bar in Side Cart (eCom/Stores)**

Date Added: December 14, 2025 | **About the request:** Represent the remaining amount (for example, "Missing X$ to get free shipping") based on the configured shipping rate, encouraging shoppers to add more items to their cart.

**Resources to help you get started:**
  • [Site Plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-side-cart?apiView=SDK.md)
| | **Location-based service selection (Bookings)**

Date Added: February 13, 2025 | **About the request:** Add a visual interface for locations synced with Wix Bookings. When a site visitor selects a location, show a list of services specific to that location. This feature enhances user experience by making service discovery easier and more intuitive.

**Resources to help you get started:**
  • [Bookings Services](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/introduction.md)
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md)
| | **Print Daily schedule (Bookings)**

Date Added: December 14, 2025 | **About the request:** Enable business owners to generate a printable agenda of the day's appointment directly from the Booking Calendar.

**Resources to help you get started:**
  • [Dashboard menu plugin slots - More actions menu](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/supported-wix-app-dashboard-pages/wix-bookings/wix-bookings-calendar-page.md#slot-2-more-actions-menu)
  • [Query Events](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/query-events.md)
| | **Offer extras and add-ons for client services (Bookings)**

Date Added: November 1, 2024 | **About the request:** Enable customers to add extras during a booking checkout for a personalized experience, which can customize costs and timing, enhance efficiency, and boost user satisfaction.

**Resources to help you get started:**
  • [Site widget in checkout](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md)
  • [Bookings APIs](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md)
  • [Checkout APIs](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/checkout/checkout/introduction.md)
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=b358e3d3-9e4c-4596-bad1-b7415fa37990&http_referrer=custom-apps-studio)
| | **Checkout and product countdown (Ecomm, Stores)**

Date Added: November 1, 2024 | **About the request:** Implement countdown timers on checkout and product pages to encourage prompt transactions, improving sales conversions by leveraging urgency and limited-time offers.

**Resources to help you get started:**
  • [Site widget in checkout and product page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md)
  • [Discount rules API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/discount-rules/introduction.md)
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=086a1107-3d84-43cc-992e-738055e01547&http_referrer=custom-apps-studio)
| | **Shipping address verification (Stores, Restaurants)**

Date Added: November 1, 2024 | **About the request:** Implement shipping address verification during checkout to ensure accurate deliveries, reduce shipment failures, and enhance customer satisfaction by ensuring orders are delivered to the correct destination.

**Resources to help you get started:**
  • [eComm validations service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md)
| | **Size chart (Stores)**

Date Added: November 1, 2024 | **About the request:** Offer size charts on product pages to help customers select the right size, enhancing their shopping experience, reducing returns, and boosting sales confidence. Merchants desire the flexibility to create these charts using either images or tables.

**Resources to help you get started:**
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in product page
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=086a1107-3d84-43cc-992e-738055e01547&http_referrer=custom-apps-studio)
| | **Add a PDF to a product description (Stores)**

Date Added: November 1, 2024 | **About the request:** Offer a PDF download option on product pages, providing resources like spec sheets for offline review. This enhances credibility and informs purchase decisions, potentially boosting sale conversions.

**Resources to help you get started:**
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in product page
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=086a1107-3d84-43cc-992e-738055e01547&http_referrer=custom-apps-studio)
| | **Free shipping progress bar (Stores, eComm)**

Date Added: November 1, 2024 | **About the request:** Add a progress bar in the cart to show customers how close they are to qualifying for free shipping. This feature incentivizes customers to add more items, boosting average order value and increasing store revenue.

**Resources to help you get started:**
  • [Site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in checkout
  • [Discount service plugin](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md#service-plugins)
  • [Explore a relevant template](https://dev.wix.com/apps-templates/template?id=b358e3d3-9e4c-4596-bad1-b7415fa37990)
| | **Zip code locator and restriction (Restaurants, Stores)**

Date Added: November 1, 2024 | **About the request:** Apply zip code restrictions for merchants offering services across multiple locations. Allow merchants to direct users to the nearest service locations, block bookings from unauthorized areas, limit bookings by zip code on specific days, and optimize schedules. The goal of this feature is to enhance efficiency and service delivery within targeted regions.

**Resources to help you get started:**
  • [eComm validation service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/validations/validations-integration-service-plugin/introduction.md)
| ## Explore more articles - [About Developing Apps](https://dev.wix.com/docs/build-apps/develop-your-app/about-developing-apps.md#about-developing-apps) - [About Launching Apps](https://dev.wix.com/docs/build-apps/launch-your-app/about-launching-apps.md) - [The Wix Choice Program](https://dev.wix.com/docs/build-apps/launch-your-app/app-promotion/promotion-by-wix/wix-choice-program.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Developing Apps ## Article: About Developing Apps ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/about-developing-apps.md ## Article Content: # About Developing Apps To support you on your app development journey, Wix offers a wide range of tools and technologies. From planning and setup to building, launching, and managing your app, we provide everything you need. This article provides an overview of the app dashboard, development frameworks, extensions, business solutions, API integrations, access protocols, and the Wix Design System, with links to more detailed articles on each topic. [](https://manage.wix.com/account/custom-apps) ## App dashboard The app dashboard is a hub for connecting and managing your app within the Wix ecosystem. Regardless of how you build your app, your journey will likely include a visit to the app dashboard. It offers various tools to help you prepare, build, integrate, launch, and manage your app. For more information, see [About the App Dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md). ## Frameworks Wix offers several [development frameworks](https://dev.wix.com/docs/build-apps/get-started/overview/wix-s-development-frameworks.md) for building apps, each providing a comprehensive development model. Which one you choose depends on how you want to build your app and who manages its hosting. Your first decision is who manages hosting and deployment: - **Wix-managed**: Wix hosts and deploys your app on the Wix cloud, with no additional setup or cost. You can build a Wix-managed app in two ways: - **Wix CLI**: Code-first development with Wix’s React/Node.js stack. - **Wix Blocks**: Visual, low-code development in Wix’s native app editor. - **Self-managed**: You host your app yourself on your own infrastructure, using any tech stack.
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/about-wix-harmony-and-blocks.md).
For more information, see [About Development Frameworks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/about-development-frameworks.md#about-development-frameworks). ## Extensions An [extension](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) is a specific functionality of an app, categorized into two broad types: frontend and backend. Both frontend and backend extensions are typically developed using one of Wix’s development frameworks. Frontend extensions include site extensions and dashboard extensions, focusing on user interface enhancements. Backend extensions, on the other hand, enhance backend capabilities through service plugins, schema plugins and notifications. Apps can incorporate multiple extensions that work together to offer diverse features across various user interfaces and backend services. For more information, see [About extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md#about-extensions) and [Map your app's functionality to Wix's extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/map-your-app-s-functionality-to-wix-s-extensions.md) ## App Settings App settings define how the app behaves during and after installation. For example, you can specify the destination where the site owner is directed after app installation completes. With these settings, you can define and manage app-level configuration conveniently in one place. For more information, see [App Settings](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/app-settings.md). ## Business Solutions Business solutions are native apps built by Wix that provide capabilities like eCommerce, running events, writing blogs, managing restaurants, and more. Users add Wix business solutions to their sites by installing these apps. Your app can integrate with Wix's business solutions through plugins or APIs. For more information, see [About Wix Business Solutions](https://dev.wix.com/docs/build-apps/develop-your-app/wix-business-solutions/about-wix-business-solutions.md#about-wix-business-solutions). ## API Integrations With Wix APIs, you can transfer data, extend Wix business solutions, and customize your app's interactions with site visitors. Each API includes events, which are automated responses sent to your app when specific site actions occur. Wix offers a variety of API technologies, including REST, JavaScript SDK, GraphQL, and Velo, to suit your app's requirements. These APIs enable integration with different areas of the Wix ecosystem. For more information, see [About Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/wix-business-solutions/about-wix-business-solutions.md#about-wix-business-solutions).
**Tip:** We recommend subscribing to [app management events](https://dev.wix.com/docs/rest/app-management/app-instance/introduction.md), such as [App Instance Installed](https://dev.wix.com/docs/rest/app-management/app-instance/app-instance-installed.md), [App Instance Removed](https://dev.wix.com/docs/rest/app-management/app-instance/app-instance-removed.md), and [Paid Plan Purchased](https://dev.wix.com/docs/rest/app-management/app-instance/paid-plan-purchased.md). These events notify your app when it's installed or removed from a site, allowing you to perform essential tasks like saving instance data, provisioning resources, or cleaning up when users uninstall your app. Learn more [about events](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/events-and-webhooks/about-events.md).
## Access Access to Wix APIs requires successful authentication and authorization. Authentication confirms the identity of the entity making the request, while authorization determines the permitted actions for that identity. Recognized identities include site visitors, site members, Wix users (or admins), and Wix apps. Third-party apps integrating with Wix APIs need to authenticate following the [OAuth](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-oauth.md) protocol. Authorization protects your site data by ensuring that only authorized users can perform specific actions. For more information, see [About Access for Wix Apps](https://dev.wix.com/docs/build-apps/develop-your-app/access/about-access.md#about-access-for-wix-apps). ## App instances When a Wix user installs your app on their site, Wix generates an app instance. An app instance represents a unique installation of your app on a specific site, identified by an `instanceId`. This ID is essential for identifying users, managing site-specific data, and handling billing logic for paid apps. The app instance also provides useful information such as the site owner's email, the app's billing cycle, and the purchased pricing package. You can retrieve app instance data through webhooks, API calls, or encoded query parameters. For more information, see [About App Instances](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances.md). ## Wix Design System The Wix Design System is a comprehensive toolkit for developers and designers to craft beautiful and intuitive product experiences. The Wix Design System offers a range of resources, including React libraries, design patterns, Figma kits, component documentation, interactive playgrounds, color palettes, icons, fonts, and more. For developers, the Wix Design System includes a React library of core reusable components. For designers, the Wix Design System includes a Figma kit to integrate Wix design assets into their design workflow. For more information, see [About the Wix Design System](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md). ## Pricing and billing If you plan to monetize your app through the Wix App Market, you'll need to set up pricing plans and implement the corresponding logic in your app. While Wix handles billing and payment processing, it's your responsibility to code the behavior that differentiates between pricing tiers. This includes identifying which plan a user has installed, restricting access to premium features for free or lower-tier users, and providing clear upgrade paths. For more information, see [Monetize Your App](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-monetizing-your-app.md). ## See also - [About the App Dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md) - [About Development Frameworks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/about-development-frameworks.md#about-development-frameworks) - [About Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md#about-extensions) - [Map your app's functionality to Wix's extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/map-your-app-s-functionality-to-wix-s-extensions.md) - [About Wix Business Solutions](https://dev.wix.com/docs/build-apps/develop-your-app/wix-business-solutions/about-wix-business-solutions.md#about-wix-business-solutions) - [About Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/wix-business-solutions/about-wix-business-solutions.md#about-wix-business-solutions) - [About Access for Wix Apps](https://dev.wix.com/docs/build-apps/develop-your-app/access/about-access.md#about-access-for-wix-apps) - [About the Wix Design System](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the App Workspace ## Article: About the Unified Workspace ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-app-workspace.md ## Article Content: # About the App Workspace
__Alpha:__ The [AI app builder](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-building-apps-with-ai.md) is currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
> **Note:** The app workspace is being gradually rolled out and isn't available to all users yet. The app workspace is your central development environment for building Wix apps. You can configure your app's settings and permissions in the [App Dashboard](#app-dashboard) tab, edit your app's files in the [Code](#code) tab's VS Code-based editor, and view your app in different contexts with the [Preview](#preview) tab. A built-in [AI chat](#ai-chat) on the left side helps you build and refine your app. ![The app workspace](https://wixmp-833713b177cebf373f611808.wixmp.com/images/68208b3f79dbbe8d4e803fa17c459dd8.png) ## App Dashboard The [**App Dashboard**](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/about-the-app-dashboard.md) tab provides a management hub for your app. In the app dashboard, you can: - [Configure pricing plans](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md). - [Promote your app](https://dev.wix.com/docs/build-apps/launch-your-app/app-promotion/about-app-promotion.md). - [Set up authentication](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-oauth.md). - [Add permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md). ## Code The [**Code tab**](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-code-tab.md) is a VS Code-based development environment where you can edit your app's files directly. You can also work locally using [GitHub integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md). The Code tab only shows code for Wix-managed extensions built with the [Wix CLI](https://dev.wix.com/docs/wix-cli.md). If you're building a [self-managed app](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/about-self-hosting-for-wix-apps.md), your extension code lives on your own infrastructure and doesn't appear here. You can manage all of your app's extensions, including self-managed ones, from the **Extensions** page in the [App Dashboard](#app-dashboard). > **Note:** The Code tab isn't supported in Safari. ## Preview The **Preview** tab lets you view your app in different contexts without leaving the workspace. It provides the following views: - **Dashboard Page**: View your [dashboard extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) as a Wix user. - **Market Listing**: View how your app will appear in the Wix App Market. - **Editor**: View how your app behaves when integrated with the editor. - **Site**: View your [site extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md) from the perspective of site visitors. ## AI chat The AI chat is available on the left side of the workspace. You can use it to [build a new app](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/get-started-with-the-ai-app-builder.md), edit existing code, ask questions, and get code explanations. The AI agent can read your project files and make changes directly in the editor. For self-managed apps, the AI chat starts collapsed. Click the toggle button in the top left to open it. For tips on getting the best results from the AI chat, see [AI prompts](https://dev.wix.com/docs/api-preview/building-apps-with-ai/get-started/best-practices-for-ai-prompts.md) and [supported features](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-the-ai-app-builder.md). ## Collaboration > **Note:** This feature is not currently supported. Multiple developers can work on the same app simultaneously. [Add collaborators](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/manage-collaborators.md) in the App Dashboard to give other developers access. Each collaborator gets their own workspace. The App Dashboard tab is shared, but each developer has an independent [Code tab](#code) and [AI chat](#ai-chat). To share code between collaborators, sync with [GitHub](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md). Current limitations: - Only the app owner can connect to the [GitHub repo](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md). Collaborator access to the same repo isn't yet supported. - Each developer has their own AI chat. Conversations aren't shared between collaborators. - Conflict handling for simultaneous edits isn't yet supported. ## See also - [Build an App with AI](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/get-started-with-the-ai-app-builder.md) - [About GitHub Integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md) - [About Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Code Tab ## Article: About the Code Tab ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-code-tab.md ## Article Content: # About the Code Tab
__Alpha:__ The [AI app builder](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-building-apps-with-ai.md) is currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
The **Code** tab is a VS Code Web-based development environment in the [app workspace](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-app-workspace.md) where you can edit your app's code directly in the browser. > **Note:** The Code tab isn't supported in Safari. ![The Code tab](https://wixmp-833713b177cebf373f611808.wixmp.com/images/68208b3f79dbbe8d4e803fa17c459dd8.png) ## File structure The Code tab shows Wix-managed extensions built and managed with the [Wix CLI](https://dev.wix.com/docs/wix-cli.md). These projects use [Astro](https://docs.astro.build/en/getting-started/), a web development framework optimized for performance, with React for UI components. The **src** folder holds your app's [extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) built with the Wix CLI. Each extension type has its own subfolder, such as **dashboard** or **site**. To manage all of your app's extensions, go to the **Extensions** page in the [App Dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-app-workspace.md#app-dashboard).
__Caution:__ Only edit files that are in the **src** folder. Editing other files may cause unexpected behavior or damage to your project.
## IDE features The Code tab includes standard VS Code Web features like syntax highlighting, code navigation, and multi-file editing. Changes are saved automatically every few seconds. ## AI chat The AI chat sits on the left side of the Code tab. You can request code changes, ask questions, and get explanations. The AI agent reads your project files and makes edits directly in the editor. To generate dashboard pages, the AI agent uses [Auto Patterns](https://dev.wix.com/docs/build-apps/develop-your-app/design/auto-patterns/about-auto-patterns.md), a configuration-driven library that lets you build pages using JSON instead of writing React components manually. ## Wix Logs **Wix Logs** is a panel in the bottom area of the Code tab, alongside **Problems**, **Output**, **Debug Console**, and **Terminal**. It centralizes [Wix CLI](https://dev.wix.com/docs/wix-cli.md) process logs into a single, timestamp-sorted stream so you can monitor and debug what the Wix CLI is doing during development. ![The Wix Logs panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1f44ae25dff9be76d18852644fdd9006.png) ### Filters and toolbar Use the toolbar controls to focus on the logs you need: - **Short**: Toggles the timestamp format between short (`HH:MM:SS.mmm`) and full ISO. - **Producers**: Filters logs by the part of the CLI that generated them. - **Levels**: Filters logs by severity, which includes`error`, `warn`, `info`, or `debug`. - **Search**: Searches logs. - **Pause** (‖): Pauses the log stream. - **Clear** (≡): Clears the current log output. - **Refresh** (↺): Reloads the log stream. ## GitHub integration You can connect your app project to a GitHub repository directly from the Code tab. This gives you version control, collaboration, and the flexibility to work in any external IDE. Learn more about [GitHub integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md). ## See also - [About the App Workspace](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-app-workspace.md) - [About Building Apps with AI](https://dev.wix.com/docs/api-preview/building-apps-with-ai/overview/about-building-apps-with-ai.md) - [About Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) - [About Auto Patterns](https://dev.wix.com/docs/build-apps/develop-your-app/design/auto-patterns/about-auto-patterns.md) - [Why Astro?](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md#why-astro) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the App Dashboard ## Article: Dev Center Overview ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/about-the-app-dashboard.md ## Article Content: # About the App Dashboard The [app dashboard](https://manage.wix.com/account/custom-apps) is a hub for connecting your app to the [Wix ecosystem](https://dev.wix.com/docs/build-apps/get-started/overview/the-wix-ecosystem.md) and managing it. Regardless of how you are building your app, your journey will most likely include a visit to the app dashboard. ## Working in the app dashboard The menu on the left of the dashboard guides you through the tasks for setting up your app. You can progress through most tasks in any order, and return to tasks later, according to your needs. Generally, the order of the tasks are: 1. [Plan](#which-tasks-must-i-do-which-tasks-can-i-skip) 1. [Get started](#getting-started) 1. [Develop and build](#developing-and-building-your-app) 1. [Integrate](#integrating-your-app) 1. [Launch](#launching-your-app) 1. [Manage](#managing-your-app) You will learn more about each task below. But first... it is important to note that you can skip tasks you don't need. ## Which tasks must I do? Which tasks can I skip? The tasks you do, and the tasks you skip, are based on the [framework](https://dev.wix.com/docs/build-apps/get-started/overview/wix-s-development-frameworks.md) you choose and if your app will be public. For example, if your app is private, you can skip many of the options in the Launch task, as there is no need to price, market, or handle payments for your app. Here are ideas for which tasks to do in the app dashboard, depending on the type of app you are building.
Public self-managed app - published in the Wix App Market This is a basic flow for launching a public, self-managed app that you can market and sell. | Task | Options to Consider | How | | ----- | ------------- | -- | | Get started | Log in and set up your app | app dashboard | | Develop and build | Add extensions
OAuth
Permissions
Webhooks
Test | app dashboard | | Integrate | Code and design | Using your own tech stack | | Launch | Translations
Marketing
Pricing
Contact info
Security & privacy
Payouts
Coupons
Collaborators
Company info
Submit | app dashboard | | Manage | Versions
Reviews
Stats | app dashboard |
Private enterprise app with Wix Blocks This scenario shows that: - You can share an app in your enterprise, perhaps for code reuse purposes, without ever marketing or selling your app. - You can extend Wix functionality using a combination of both Wix Blocks and other extensions in the same app. - Your enterprise app can accommodate translations even if it is never launched to the app market. | Task | Options to Consider | How | | ----- | ------------- | --- | | Get started | Log in and set up your app | app dashboard or Wix Blocks | | Develop and build with Wix Blocks | Add [supported Wix Blocks extensions](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/supported-extensions/about-extensions-in-blocks.md) | Wix Blocks | | Develop and build | Add [extensions without Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/supported-extensions/about-extensions-in-blocks.md)
OAuth
Permissions
Webhooks
Test | app dashboard | | Integrate | Code and design | Wix Blocks | | Launch | Translations | app dashboard | | Manage | Versions | Wix Blocks or app dashboard |
Private app with Wix Blocks This task demonstrates that there are scenarios where you can build an app without using the app dashboard at all. - Because this scenario is a private app, there is no need to launch or market the app. - Depending on what your app needs to do, it is possible that all the tasks for designing, coding, and managing your app can be done entirely with Wix Blocks. | Task | Options to Consider | How | | ----- | ------------- | -- | | Get started | Set up your app | Wix Blocks | | Develop and build | Add [supported Wix Blocks extensions](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/supported-extensions/about-extensions-in-blocks.md) | Wix Blocks | | Integrate | Code and design | Wix Blocks | | Manage | Manage versions | Wix Blocks |
Public app with Wix CLI, published in the Wix App Market This is a basic flow for launching a public app created with the Wix CLI that you can market and sell. - Some of the development and configuration that is available in the app dashboard is also available in the CLI. - You can extend Wix functionality using a combination of extensions developed in both the Wix CLI elsewhere in the same app. | Task | Options to Consider | How | | ----- | ------------- | --- | | Get started | Log in and set up your app | Wix CLI | | Develop and build | Permissions
Webhooks
Add extensions without CLI
Permissions
Webhooks | app dashboard | | Develop and build | Add [supported CLI extensions](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/about-extensions-in-the-wix-cli.md)
Test
Create version | Wix CLI | | Integrate | Code, design, and preview | Wix CLI | | Launch | Translations
Marketing
Pricing
Contact info
Security & privacy
Payouts
Coupons
Collaborators
Company info | app dashboard | | Manage | Versions
Reviews
Stats | app dashboard |
Public app that does not require external integrations, published in the Wix App Market This type of public app is developed, marketed, and managed totally within the app dashboard. There is no need to use any frameworks at all. We can relate to the extensions used in this type of app as "configuration-only" apps. | Task | Options to Consider | How | | ----- | ------------- | -- | | Get started | Log in and set up your app | app dashboard | | Develop and build | OAuth
Permissions
Webhooks
Add "configuration-only" extensions such as:
- [Site pages](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md)
- [External links](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/external-links/about-external-link-extensions.md)
- [Dashboard menu plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/dashboard-menu-plugins/add-dashboard-menu-plugin-extensions.md)
- [Schema plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md)
Test | app dashboard | | Launch | Translations
Marketing
Pricing
Contact info
Security & privacy
Payouts
Coupons
Collaborators
Company info | app dashboard | | Manage | Versions
Reviews
Stats | app dashboard |
## Getting started First, log into your Wix Studio account. If you don't already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps). Once logged in, [set up](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-apps.md) an app in the app dashboard. This task lets you set up your app so you can manage it using the app dashboard. ## Developing and building your app From within the app dashboard, you can utilize a set of tools and extensions to help you build and release apps. For example, you can: - [Extend Wix functionality](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) - [Sign up to webhooks](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/events-and-webhooks/about-webhooks.md) - [Set up permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/configure-permissions-for-your-app.md) - [Set up OAuth](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-oauth.md) - [Test the app locally](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md) ## Integrating your app You generally develop, code, and design your app outside of the app dashboard. This task is about integrating what you developed externally using the app dashboard. For example, using the app dashboard, you can: - Connect any iframes you coded externally, by providing the relevant URLs and settings data. - Integrate with any custom business logic you externally coded by adding a [service plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/service-plugins/about-service-plugin-extensions.md). ## Launching your app If you are setting up a public app, use the app dashboard to: - [Price your app](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md) - [Distribute your app](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/about-app-distribution.md) - [Set up your Market listing](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/about-market-listings.md). If your app depends on a Wix business solution being installed on a site for your app to work, [add the dependency](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/add-your-app-audience-info.md) in the app dashboard when setting up your market listing. This prevents users from installing your app until the relevant business solution is installed. - [Create promotional assets](https://dev.wix.com/docs/build-apps/launch-your-app/app-promotion/about-app-promotion.md) - [Submit your app for Wix approval](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/submit-your-first-app-version.md). Check the bottom left of the app dashboard to see if there are any blockers. Did you skip an essential step? Is information missing? Click **Fix blockers** for a summary of the blockers to fix, including suggestions you might want to consider. ## Managing your app Once your app is up and running, you can make changes. For example, you can: - Respond to [reviews](https://dev.wix.com/docs/build-apps/manage-your-app/user-support/user-reviews.md) - Check [analytics](https://dev.wix.com/docs/build-apps/manage-your-app/data-and-analytics/bi-events.md) - Process [refunds](https://dev.wix.com/docs/build-apps/manage-your-app/user-support/issue-a-refund.md) - Track [statistics](https://dev.wix.com/docs/build-apps/manage-your-app/data-and-analytics/app-stats.md) - View [transactions](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/payouts-dashboard.md) - Provide [coupons](https://dev.wix.com/docs/build-apps/launch-your-app/app-promotion/self-promotion/create-a-coupon.md) for your app - Update your [company information](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/add-your-company-info.md) - Determine who can [collaborate](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/manage-collaborators.md) on your app - Make sure your app's users can [contact](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/add-your-contact-info.md) you ## See also - [Tutorial | Set Up an App with the App Dashboard](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-app-dashboard.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add Your Contact Info ## Article: Add Your Contact Info ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/add-your-contact-info.md ## Article Content: # Add Your Contact Info Ensuring that your app's users and Wix can easily reach you is crucial for maintaining a successful app on the Wix App Market. To achieve this, it's important to keep your contact info accurate and up-to-date. To add or update your contact info: 1. Go to the **Contact Info** page in your app's dashboard. 2. Provide at least one email address for each field: - **Support email**: This email is essential for handling user support requests, both from users and the App Market team. It is displayed in the App Market for users seeking assistance. You can input up to 10 email addresses, separated by commas. - **Billing email**: All billing-related info, including periodic reports on earnings and invoice requests, is directed to this email address. Only one email address is permitted in this field. If multiple team members require access to billing information, we suggest creating a shared group email address. - **Urgent email and number**: Critical support requests from the App Market team and important updates regarding features and changes are communicated through this channel. You can enter up to 10 email addresses, separated by commas. While email will serve as the primary mode of contact, in exceptional circumstances we may use the phone number provided. 3. Click **Save**.
**Tip:** As you enter email addresses, the last one entered is registered once you click outside the field, add a comma after it, or press Enter.
## See also - [Add your company info](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/add-your-company-info.md) - [Manage team collaborators](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/manage-collaborators.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Manage Collaborators ## Article: Manage Collaborators ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/manage-collaborators.md ## Article Content: # Manage Collaborators As an App Owner, you can invite people to work on your app, assign them roles, and set their permissions in the App Dashboard. ## Role types - **App Owner**: Can perform any action in the account and is considered an admin. Only the Owner can request to close an account. - **App Co-owner**: Can edit, publish and manage the app, including creating extensions, adding webhooks and permissions, and inviting people. They can’t archive or change ownership of the app.
Note: If you need to transfer Owner rights to another user let us know and we'll do it for you.
## Invite a new collaborator 1. Go to the **Collaborators** page in your app’s dashboard. 2. Click **+ Invite Collaborators** in the top right corner. 3. Enter the email address of the collaborator. To add multiple invitees, enter each email separated by a comma. 4. Click **Send Invite**.
Tip: After you've sent the invite you can also click the **More Actions** icon next to the relevant collaborator to resend the invite or get an invite link.
## Remove a collaborator 1. Go to the **Collaborators** page in your app’s dashboard. 2. Click the **More Actions** icon next to the relevant collaborator. 3. Click **Remove**. ## See also - [Add your contact info](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/add-your-contact-info.md) - [Add your company info](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/add-your-company-info.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: App Settings ## Article: App Settings ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/app-settings.md ## Article Content: # App Settings The **App Settings** page lets you customize your app's post-installation behavior, both for the app in general and for specific extensions. ## Post-installation navigation When a Wix user installs your app, the page that they're directed to by default to depends on your app's extensions: - If your app has no extensions, Wix users are directed to the [Manage Apps](https://www.wix.com/my-account/site-selector/?buttonText=Select%20Site&title=Select%20a%20Site&autoSelectOnSingleSite=true&actionUrl=https:%2F%2Fwww.wix.com%2Fdashboard%2F%7B%7BmetaSiteId%7D%7D%2Fmanage-installed-apps) page in their dashboard. - If your app has at least one site extension, such as a site widget or a site page, Wix users are directed to the editor. - If your app has only dashboard page extensions, Wix users are directed to the first dashboard page created in your app. In the **Post-installation navigation** section, you can change the default behavior. If you've added one or more dashboard page extensions, you can choose to direct Wix users to one of these pages. ## Extension installation behavior In addition to navigation, you can configure post-installation behavior for specific extensions. For example, you can choose to add a site widget to a site page upon installation, instead of requiring the Wix user to add the widget themselves. The **Extension installation behavior** section directs you to the [**Extensions**](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fextensions) page in the app dashboard, where you can configure specific behavior for each extension. ## See also - [About the App Dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/app-dashboard-setup/about-the-app-dashboard.md) - [About Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Share Your App with an Install Link ## Article: Share Your App with an Install Link ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/share-your-app-with-an-install-link.md ## Article Content: # Share Your App with an Install Link An Install Link enables Wix site-creators to install your app on their site. This is useful when you want to share your app with a small number of specific site creators, early adopters, or use it for testing purposes. ## Generate an Install Link 1. Go to the **Custom Apps** page. 2. Click the **More Actions** menu next to the app you want to share. 3. Click **Share install link** to install app. 4. Click **Generate Link**. 5. Click **Copy Link**. ![Share](https://wixmp-833713b177cebf373f611808.wixmp.com/images/812f9b020d5fd1f1dfbe08f770140d3c.png) ## Disable an Install Link There can only be one Install Link at any given time. If you want to disable an Install Link, create a new link and it will disable the old one. ## See also - [Create a market listing](https://dev.wix.com/docs/build-apps/launch-your-app/market-listing/about-market-listings.md) - [About app promotions](https://dev.wix.com/docs/build-apps/launch-your-app/app-promotion/about-app-promotion.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Code Identifier ## Article: About the Code Identifier ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/app-dashboard/code-identifier.md ## Article Content: # About the Code Identifier
__Alpha:__ The [AI app builder](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-building-apps-with-ai.md) is currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
The code identifier is a unique, JavaScript-compatible name for your app and its extensions. ## Set your code identifier When you build an app using AI, your namespace is generated automatically. Wix then suggests a code identifier based on that namespace. You can accept the suggestion or enter your own value. > **Note:** In the Wix CLI flow, you choose your namespace manually during app creation. Your code identifier must follow these rules: - Use only JavaScript-friendly characters. These include letters (`A-Z`, `a-z`), digits (`0-9`, but not as the first character), underscores (`_`), and dollar signs (`$`). - Avoid using the word "Wix". - Use camelCase for readability. Once set, you can't change your code identifier. ## View your code identifier To view your app's code identifier: 1. Go to [Custom Apps](https://manage.wix.com/account/custom-apps). 1. Select the app whose code identifier you want to view. 1. Click the **More Actions** icon at the top of the page. 1. Select **View ID & Keys**. Your app's code identifier appears there. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About GitHub Integration ## Article: About GitHub Integration ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md ## Article Content: # About GitHub Integration
__Alpha:__ The [AI app builder](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-building-apps-with-ai.md) is currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
GitHub integration connects an app project to a dedicated GitHub repository. This gives you GitHub's version control and collaboration capabilities, as well as the flexibility to work in whatever IDE you prefer. ## Code in your preferred IDE Develop an app using your preferred IDE. Wix sets up a repository that you can clone and develop in using standard development workflows. ## Work with multiple collaborators at once You can let others interact with an app's code by inviting them as [collaborators](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/inviting-collaborators-to-a-personal-repository) on your GitHub repository. Multiple developers can work on an app's code at once. ## Version control GitHub integration enables version control for your project. Your app reflects the main branch of your repository. To sync your local changes, merge them to the main branch. Wix saves changes to the app's code: - Periodically while working in the **Code** tab. - When the AI agent makes a change to the app. Saved changes are tracked as commits in your GitHub repository, giving you a clear history of edits and the ability to revert to previous versions when needed. When multiple sources edit the same files simultaneously, conflicts can occur. If you push changes to the repository while the AI agent makes changes to the same files, the AI agent shows a rejection message. If your local clone of the repository has files that aren't up to date and you push changes to those files, you'll get a rejection message in your IDE. ## File structure AI app projects are built using [Astro](https://docs.astro.build/en/getting-started/), a web development framework optimized for performance. The UI components are written in React. The **src** folder contains [extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md) that add functionality to your app. Each extension type has its own subfolder, such as **dashboard** or **site**.
__Caution:__ Only edit files that are in the **src** folder. Editing other files may cause unexpected behavior or damage to your project.
## Get started To get started with GitHub integration, [set up GitHub integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/set-up-git-hub-integration.md) to connect your project to a repository. > **Note**: GitHub integration only supports creating new repositories. You can't reconnect to an existing repository. ## See also - [About the Code tab](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/about-the-code-tab.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Set Up GitHub Integration ## Article: Set Up GitHub Integration ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/set-up-git-hub-integration.md ## Article Content: # Set Up GitHub Integration
__Alpha:__ The [AI app builder](https://dev.wix.com/docs/build-apps/develop-your-app/build-with-ai/about-building-apps-with-ai.md) is currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
## Connect to GitHub Set up [GitHub integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md) to connect an app project to a dedicated repository created in your GitHub account. This enables version control, collaboration, and the ability to edit the app in any external IDE. To set up GitHub integration: 1. Click ![connect to GitHub icon](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7d8c99e0bbac520a3e83e6dba4078b55.png) to open the GitHub integration menu. 2. In the menu, click **Connect to GitHub**. A new window opens. 3. Log in to GitHub, if needed. 4. Select your GitHub account or organization, and then click **Install & Authorize**. After installation, the window closes. 5. The GitHub integration menu now shows your connected account and a repository name. You can type in a different name for the repository. After you choose a name, click **Create Repository**. 6. To create a local clone of the repository, click **Clone to your computer** in the menu. > **Note**: GitHub integration only supports creating new repositories. You can't reconnect to an existing repository. ## Disconnect from GitHub If you need to, you can disconnect an app from GitHub. When you do this, your app retains the code from the most recent commit to its GitHub repository's default branch. After disconnecting, the repository still exists in GitHub, but changes to it aren't reflected on the app. To disconnect an app from GitHub: 1. Click ⋯ **More Actions** in the GitHub integration menu, and then click **Disconnect from GitHub**. A confirmation window opens. 2. In the confirmation window, click **Disconnect from GitHub**.
**Important:** Once you disconnect an app from its GitHub repository, you can't reconnect the app to that repository again. If you reconnect the app to GitHub later, a new repository is created.
## See also - [About GitHub Integration](https://dev.wix.com/docs/build-apps/develop-your-app/app-workspace/git-hub-integration/about-git-hub-integration.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Extensions ## Article: About Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/about-extensions.md ## Article Content: # About Extensions An [extension](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) is a specific functionality of an app. An app can have multiple extensions that work together to provide various features across different user interfaces and backend services. Most extensions, whether frontend or backend, are created using one of Wix’s [development frameworks](https://dev.wix.com/docs/build-apps/get-started/overview/wix-s-development-frameworks.md#wixs-development-frameworks). You can view your dashboard extensions in the **Extensions** tab in the [app dashboard](https://manage.wix.com/account/custom-apps). ## Frontend extensions Frontend extensions enhance Wix sites with additional functionality, including adding site pages, draggable widgets, site plugins, embedded scripts, and dashboard interfaces. - **Site Pages:** Appear in the site's main navigation menu. They function like any other page and are composed of one or more of the app’s widgets. - **Site Widgets:** Are draggable components that you can add to and place anywhere on your site. - **Editor React Components:** Are custom site widgets built in React for [Wix Harmony](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md). The editor automatically generates settings panels and controls from the component manifest, giving Wix users a native editing experience without you building panel UI. - **Site Plugins:** Allow you to extend Wix-created apps by being placed in designated areas within the hosting app’s site widget. - **Embedded Scripts:** Allow you to inject custom HTML code into a site’s HTML. - **Dashboard Pages:** Pages that you can add to the dashboard that are only visible to site admins with the necessary permissions. - **Dashboard Modals:** Allow you to expand Wix's dashboard capabilities by to importing a modal onto a dashboard page. - **Dashboard Plugins:** Allow you to customize Wix dashboard pages by adding new functionalities to predefined slots and introducing new items to dashboard menus. This customization directs administrators to different pages or modals within the dashboard. - **Dashboard Menu Plugins:** Allow you to place a new menu item in a predefined slot, or UI placeholder, in a menu on the dashboard page of an app built by Wix. ## Backend extensions Backend extensions allow you to customize Wix's backend business logic to suit your app's needs, enhancing the value your apps provide to site owners. - **Data Collections Extensions:** Automatically create CMS data collections when your app is installed on a site. Configure collection schemas and add optional initial data. - **Schema Plugin Extensions:** Enable field extensions to Wix's objects. They enrich and extend Wix's data by adding additional fields to service objects. - **Service Plugin Extensions:** Wix-defined APIs that apps can implement to become service providers. This enables Wix to call the service during specified flows and proceed based on the response. - **Notifications:** Send predefined alerts with customizable data to users through their site dashboard or the Wix Owner mobile app. This is useful for informing users of important updates and events. - **Web Method Extensions:** Define functions in your app's backend that you can call from your frontend code. - **Automations:** Automate business processes using triggers, actions, and pre-installed automations. - **APIs:** Expose backend methods that can be called from frontend code. - **Events:** Allow you to run custom code in response to specific events that occur on a site. ## See also - [How apps extend Wix](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) - [Mapping your app's functionality to Wix's extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/map-your-app-s-functionality-to-wix-s-extensions.md) - [About Site Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md) - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) - [About Dashboard Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) - [About Backend Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/about-backend-extensions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Map Your App’s Functionality to Wix’s Extensions ## Article: Map Your App’s Functionality to Wix’s Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/map-your-app-s-functionality-to-wix-s-extensions.md ## Article Content: # Map Your App’s Functionality to Wix’s Extensions When developing a Wix app, a critical step is understanding which extensions will best deliver your app's functionality to Wix users. Start with what you want to accomplish, then select the appropriate extension types to bring your vision to life. ## Adding content to a site These extensions allow you to enhance and extend the front-end experience of Wix sites, giving Wix users more ways to engage their visitors with your app's capabilities. ### Add placeable components Need to add a component that Wix users can place anywhere on their existing pages? → Use a [site widget extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md). Ideal for interactive elements like advanced forms, product configurators, or social media displays that Wix users can position within their content. ### Create full pages Want to create a full, dedicated page for your app's experience? → Choose a [site page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md) and fill it with a site widget extension. This is perfect for comprehensive experiences like community forums, custom galleries, or specialized landing pages that need their own real estate on a Wix site. ### Implement persistent components Creating a persistent element that should always be visible? → An [embedded script extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) is your solution. Perfect for chat widgets, notification bars, or feedback buttons that "stick" to specific edges of the page. ### Track visitor behavior and metrics Tracking user behavior and page performance metrics across an entire site? → Implement an [embedded script extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) for comprehensive analytics. Perfect for capturing navigation patterns, interaction heatmaps, conversion funnels, and performance benchmarks that work consistently across all pages without requiring manual placement on each one. ### Display custom modals Building a popup or modal experience that appears conditionally? → Choose an [embedded script extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md). Great for newsletter signups, promotional announcements, or detail views that overlay the main content. ### Integrate with a Wix site element Want to enhance an existing Wix business solution with an additional UI component? → Develop a [site plugin extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) that users can place inside designated areas (called slots) within the Wix site element, to integrate directly within Wix's business tools. This allows you to add custom components to booking flows, product pages, event registrations, and more. ## Creating site building tools These extensions enhance the Wix Editor experience, giving site creators powerful new design and content creation capabilities that complement Wix's native toolset. ### Add site design tools Need to enhance the Wix Editor with custom design tools or specialized content creation capabilities? → An [Editor add-on extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/editor-extensions/about-editor-add-on-extensions.md) is perfect for advanced typography controls, specialized layout tools, or custom content creation interfaces that empower site creators with features tailored to specific design needs right within the Editor environment. ## Creating site management tools These extensions help you build powerful administrative interfaces that let Wix users manage your app's functionality directly from their Wix dashboard. ### Provide management interfaces Building a management interface for Wix users? → A [dashboard page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md) provides a dedicated space in the Wix dashboard where Wix users can manage your app's settings and operations. Create campaign management dashboards, inventory management interfaces, and much more. ### Integrate external dashboards Have functionality that lives on an external platform? → Choose an [external link extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/external-links/about-external-link-extensions.md) that allows Wix users to access your external tools directly from the Wix dashboard menu. Give Wix users one-click access to powerful tools and advanced capabilities, seamlessly accessible from their Wix environment. ### Integrate with a Wix dashboard Want to enhance existing Wix business dashboards with your tools? → Use a [dashboard plugin extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/about-dashboard-plugin-extensions.md) to integrate your functionality within Wix's business solution dashboards, like adding inventory insights to the Wix Stores inventory dashboard, or adding dynamic pricing tools to the Wix Bookings services dashboard. ### Add your pages and modals to Wix dashboard menus Want to add your dashboard pages or modals to Wix business solution menus? → Implement a [dashboard menu plugin extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/dashboard-menu-plugins/dashboard-menu-plugin-extensions.md) to place your functionality directly in relevant menu sections of Wix business solutions. For example, add a "Tax Compliance" option to the Wix Stores product dashboard menu for instant access to sales tax calculation tools and reporting features, or a "Contact Engagement" option to the Contact dashboard menu where Wix users can instantly access engagement history for their contacts. > **Note**: Learn more about [plugins and menu plugins for dashboard pages](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-plugins/supported-wix-app-dashboard-pages/about-dashboard-page-slots.md). ### Display custom modals Building a popup or modal experience that appears conditionally? → A [dashboard modal extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-modals/about-dashboard-modals.md) creates popup interfaces perfect for setup wizards or detailed views within the dashboard environment. Launch a wizard, an optimization flow, and so much more. ## Enhancing backend capabilities These extensions allow your app to integrate with Wix's backend systems, enabling powerful functionality that works behind the scenes to enhance the site's capabilities. ### Implement custom business logic Need to inject custom logic or connect to external systems? → [Service plugin extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/service-plugins/about-service-plugin-extensions.md) are the answer. These allow your app to integrate with Wix's business logic for capabilities like custom shipping calculations, third-party fulfillment, or specialized payment processing. For example, implement sophisticated tax calculation logic that applies the correct international tax rates based on customer location and product type, or create a custom order fulfillment system that routes orders to the optimal warehouse based on inventory levels, proximity to customer, and shipping cost optimization. ### Extend Wix’s data models Want to extend Wix's data models with additional information? → Implement [schema plugin extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md) to add custom fields to Wix entities. These are perfect for adding attributes to products, services, or customer profiles that Wix doesn't natively support. ### Send notifications to Wix users Have alerts for Wix users about important events or updates related to your app? → Use [notification extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/schema-plugins/about-schema-plugin-extensions.md) to send customizable notifications to Wix users through their site dashboard and/or the Wix Owner mobile app. These are ideal for alerts about completed processes or time-sensitive information without requiring them to be actively using your app. ### Enable automations Want to enable Wix users to automate repetitive tasks related to your app's functionality? → Implement [automation extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/about-automations.md) to create triggers, actions, or [pre-installed automations](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/automations/pre-installed-automations/about-pre-installed-automations.md). This allows Wix users to automate processes like sending confirmation emails, updating records, or managing workflows when specific events occur. You can provide just the trigger (event detection), just the action (what happens), or complete pre-installed automations that combine both. ## Mixing and matching for complete solutions The true power of Wix's extension framework comes from combining different extension types to create comprehensive solutions. Most successful apps utilize multiple extension types to deliver seamless experiences. For example: - A customer loyalty app might use a site widget extension to display points on the site, a site plugin extension to integrate with checkout, a dashboard page extension for program management, a service plugin extension for points calculation logic, an automation extension to automatically notify customers of awarded points when purchases are made, and notification extensions to alert Wix users when a customer has reached reward thresholds. - An inventory management solution could combine a schema plugin extension for product data enhancement, a dashboard plugin extension for inventory controls within the Wix Stores interface, a service plugin extension to connect with external warehouse systems, and an automation extension to trigger reordering when stock levels are low. - A marketing tool might use embedded scripts for promotional popups, a dashboard page extension for campaign management, notification extensions to alert Wix users about campaign performance, and automation extensions to send targeted emails when visitors take specific actions on the site. ### Extension availability by framework Keep in mind that extension availability may vary depending on which Wix development framework you're using. Not all extensions are available in every framework. For a list of supported extensions by framework: - [Supported extensions in CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/about-extensions.md) - [Supported extensions in Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/supported-extensions/about-extensions-in-blocks.md) - [Supported extensions for self-managed apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/about-self-hosted-app-extensions.md) ## See also - [Choose the Right Wix Framework for Your App Development Needs](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/choose-the-right-wix-framework-for-your-app-development-needs.md) - [About development frameworks](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/about-development-frameworks.md) - [How apps extend Wix](https://dev.wix.com/docs/build-apps/get-started/overview/how-apps-extend-wix.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Extensions ## Article: About Site Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md ## Article Content: # About Site Extensions Site extensions are front-facing interfaces that enhance Wix sites with additional functionality, such as adding site pages, widgets, Editor React Components, site plugins, and embedded scripts for custom HTML code. ## Site pages A site page extension adds a page to the user's site. The page can appear in the site's main navigation menu and behaves just like any other page. Site pages are composed of one or more of your app’s widgets, and can be comprised of elements like buttons, input fields, and images, widgets. ## Site widgets Site widgets are draggable UI components that Wix site owners can add to pages on their website. They enhance a site's functionality by displaying content or enabling site visitors to perform various tasks. When working in the site editor, site owners can adjust the size of the widget, reposition it, and customize it using its settings panel. Widgets can be added to your site by using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-widgets/build-a-site-widget-in-blocks.md), [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-widgets/add-a-site-widget-extension.md) or a [self-managed custom element](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-hosted-site-widget-extensions-with-custom-elements.md). Site widgets are [sandboxed](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md#sandboxing-in-the-editor) in the editor. Learn more about [handling sandboxing in the editor](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/handle-sandboxing-in-the-editor.md). ## Editor React Components [Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) are custom site widgets built in React for [Wix Harmony](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md), Wix's AI-powered editor. They're draggable components that Wix users can add to pages and customize through auto-generated settings panels derived from the component manifest. Editor React Components aren't supported on Wix Editor or Wix Studio sites. You can add Editor React Component extensions using the [Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md). To compare with custom element site widgets, see [Custom Elements vs. Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/supported-extensions/site/custom-elements-vs-editor-react-components.md). ## Site plugins With site plugins, you can create interactive and feature-rich widgets that seamlessly integrate into Wix’s [business solutions](https://support.wix.com/en/business-solutions-apps) such as Wix Stores and Wix Bookings, extending their functionality and user experience. Plugins are built using [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-blocks.md), which offers powerful layout and design tools, and gives you access to Velo's full-stack development platform. Wix users can easily place plugins into predefined slots (UI placeholders) within Wix apps, using the [plugin explorer](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) available in all Wix editors. Site plugins are [sandboxed](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md#sandboxing-in-the-editor) in the editor. Learn more about [handling sandboxing in the editor](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/handle-sandboxing-in-the-editor.md). ## Embedded scripts An embedded script is an app extension that injects an HTML code fragment into the DOM of a site. Unlike other extensions, embedded scripts aren't fully configured by default during app installation and have an [additional setup](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) step to embed the code fragment. For example, an embedded script can be used to listen for predefined Wix events, as well as to report and listen for custom events. ## See also - [About site pages](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md) - [About site widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) - [About site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) - [About embedded scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About MPA Architecture ## Article: About MPA Architecture ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-mpa-architecture.md ## Article Content: # About MPA Architecture Wix sites use Multi-Page Application (MPA) architecture to deliver fast, reliable page loading. Understanding how MPA works helps you build [site extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md) that perform optimally. > **Note:** MPA architecture is gradually replacing Single-Page Application (SPA) architecture across Wix sites. Build your site extensions for MPA architecture to ensure optimal performance. Extensions optimized for MPA architecture won't affect the performance of sites still using SPA architecture. ## What is MPA? MPA is a website architecture in which the browser loads a separate HTML document for each page. When site visitors navigate to different pages on a Wix site, the browser loads a new HTML document for each page, similar to traditional websites. This differs from SPAs, where only the initial page loads HTML, and subsequent navigation triggers API calls to update content dynamically. ## How MPA improves site visitor experience MPA architecture provides several benefits for Wix sites: - Faster navigation: Each page loads independently, preventing slow API calls from blocking the entire navigation experience. - Better performance measurement: Standard web performance metrics measure page navigations and include them in Core Web Vitals ([CrUX](https://developer.chrome.com/docs/crux)) data. - Improved reliability: Issues with individual pages don't affect a site's navigation performance. ## Building MPA-compatible site extensions When you develop site extensions, keep in mind that each page loads independently. When site visitors navigate between pages, navigation clears any data you store in memory. Use the [session storage API](https://dev.wix.com/docs/sdk/frontend-modules/storage/session.md) instead of [memory storage](https://dev.wix.com/docs/sdk/frontend-modules/storage/memory.md) to maintain data across page navigations. Session storage persists data throughout site visitors' browser sessions, ensuring your extension works seamlessly as site visitors move between pages on a site. ## Testing your app To test your site extensions with MPA navigation, add this query parameter to your site’s URL: `?experiments=specs.thunderbolt.forceMpaNavigation`. This forces MPA navigation even on sites that normally use SPA mode. Test page loads, reloads, and back navigation to confirm your extensions reinitialize or persist data as expected. ## See also - [About Site Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md) - [Storage SDK](https://dev.wix.com/docs/sdk/frontend-modules/storage/introduction.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Handle Sandboxing in the Editor ## Article: Handle Sandboxing in the Editor ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/handle-sandboxing-in-the-editor.md ## Article Content: # Handle Sandboxing in the Editor [Site widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) and [site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) run in sandboxed environments when editing and previewing a site. This means they're treated as if they come from a different domain, and Wix does not support the `allow-same-origin` directive in the sandbox attribute. As a result, your site plugin or widget can't access same-origin resources in the editor. This restriction impacts several key web storage and caching APIs: * [Cookie Store API](https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API), such as [`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) * [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API), such as [`Window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [`Window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) * [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) * [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) You may encounter additional issues: - [Chrome Local Network Access error](#chrome-local-network-access-error): Chrome may block rendering due to local network access restrictions. - [Failed access to browser APIs](#failed-access-to-browser-apis): certain browser APIs (like `localStorage`) are unavailable in the editor. - [CORS error](#cors-error): self-managed scripts may fail to load due to missing cross-origin headers.
__Important:__ The format of the Wix editor URL is changing due to new cookies introduced for authorization. The new URL format is `{username}-{sitename}.{editor|studio|harmony}.wix.com`. If your app uses CORS and whitelists specific origins, update your allowed origins to match the new format to avoid blocked requests.
## Chrome Local Network Access error In Chrome, you may encounter a Local Network Access permissions error when rendering site plugins in the editor. To resolve: 1. Navigate to `chrome://flags/#local-network-access-check`. 2. Disable the **Local Network Access Checks** setting. 3. Restart your browser. Disabling this permission may subject you to [certain security risks](https://developer.chrome.com/blog/local-network-access). ## Failed access to browser APIs When your widget runs in the editor or preview mode, some browser APIs are unavailable to access. If your code tries to access `localStorage`, cookies, or other restricted APIs without checking the current mode, it will throw runtime errors. The code examples in this task use the [Web Storage API `localStorage` property](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage). However, you can apply these steps to all sandboxed browser APIs and properties. If you are working with the cookies API, make sure to comply with [GDPR and data protection regulations](https://dev.wix.com/docs/build-apps/launch-your-app/legal-and-security/gdpr-compliance/about-gdpr-and-data-protection.md). There are 2 ways to handle this issue: check the current mode before each API call, or create a reusable flag for multiple API calls. ### Option 1 | Check the current mode before a single API call 1. Import `window` from the [Site Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md). 2. Check the current mode with [`viewMode()`](https://dev.wix.com/docs/sdk/frontend-modules/window/view-mode.md) before accessing the browser API: ```javascript import { window } from "@wix/site-window"; if ((await window.viewMode()) === "Site") { const item = localStorage.getItem('myKey'); } else { // Mock storage or modify your API usage accordingly } ``` ### Option 2 | Create a reusable flag for multiple API calls 1. Import `window` from the [Site Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md). 2. Set a flag using [`viewMode()`](https://dev.wix.com/docs/sdk/frontend-modules/window/view-mode.md) to indicate if the current mode is site mode: ```javascript import { window } from "@wix/site-window"; const viewMode = await window.viewMode(); const isSiteMode = viewMode === "Site"; ``` 3. Use the flag before accessing browser APIs: ```javascript if (isSiteMode) { const item = localStorage.getItem('myKey'); } else { // Mock storage or modify your API usage accordingly } ``` ## CORS error When hosting your site widget script on a 3rd-party server (such as Netlify), you can encounter a CORS error: ```curl Access to script at 'https://example.com/assets/widget.js' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. ``` This error occurs because the widget runs in a sandboxed iframe context that has an opaque origin (`null`). The browser blocks cross-origin script requests unless the hosting server explicitly allows them. To fix this, configure your hosting provider or server to return the `Access-Control-Allow-Origin` header for your script files. Below is the example script for Netlify in the `netlify.toml` file: ```toml [[headers]] for = "/assets/*" [headers.values] Access-Control-Allow-Origin = "*" Access-Control-Allow-Methods = "GET, OPTIONS" ``` ## See also - [About Site Widget Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) - [About Site Plugin Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) - [About the Site Window API](https://dev.wix.com/docs/sdk/frontend-modules/window/introduction.md) - [`viewMode()`](https://dev.wix.com/docs/sdk/frontend-modules/window/view-mode.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Migrate to a New Site Widget or Plugin Extension ## Article: Migrate to a New Site Widget or Plugin Extension ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/migrate-to-a-new-site-widget-or-plugin-extension.md ## Article Content: # Migrate from a Previous Site Widget or Plugin Extension to an Updated Extension If you've built a new site widget or plugin extension to replace an existing one in your app, follow these instructions so Wix users can transition smoothly. You can't delete the old extension—doing so breaks the widget or plugin for everyone who already has it installed. Keep both extensions in your app: existing installations keep using the original, while new installations get the updated extension automatically.
__Important:__ Keep the previous extension in your app. Deleting it breaks the widget or plugin for Wix users who already installed it on their sites.
## Step 1 | Configure the previous extension Adjust the previous extension's installation settings so that new installs don't add it, while keeping it working for Wix users who already have it installed. To configure the previous extension: 1. In the [**app dashboard**](https://manage.wix.com/account/custom-apps), go to **Develop** > **Extensions** in the left menu. 2. Next to the previous site widget or plugin extension you want to replace, click **Configure**. 3. Configure the following: **For widgets:** * **Where is the widget added when the app is installed?**: Select **Not added automatically** so the widget isn't automatically added to sites that install your app later. * **Show this widget in the Add Elements panel**: Turn off this option so the previous widget doesn't appear in the **Add Elements** panel for Wix users. **For plugins:** * **Plugin name**: Rename the plugin and add "Old" or "Previous" to the name so the previous plugin doesn't appear as a viable option in the plugin explorer for Wix users. * **Add this plugin automatically**: Turn off this option so the plugin isn't automatically added to sites that install your app later. 4. Click **Save**. ## Step 2 | Notify Wix users who already installed the previous extension about the updated extension Tell Wix users who already have the previous extension installed that an updated version is available. You can show the message in either of these places: * **Settings panel:** Add a notification banner or message at the top of the previous extension's settings panel. * **In the extension:** Show a message inside the widget or plugin that's visible only in the editor, not on the live site. Your notification should: * Explain that an updated extension with improved features is available. * Tell Wix users where to find the updated extension: * For widgets: In the **Add Elements** panel * For plugins: In the plugin explorer * Provide instructions on how to replace the previous extension with the updated extension. ![Notification example](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0c4c24b33b5210594f50794aef76cdf0.png) ## Step 3 | Configure the updated extension Configure the updated extension to be visible to Wix users and automatically added to sites when Wix users install your app for the first time. To configure the updated extension: 1. In the **app dashboard**, go to **Develop** > **Extensions** in the left menu. 2. Next to the updated site widget or plugin extension, click **Configure**. 3. Configure the following: **For widgets:** * **Where is the widget added when the app is installed?**: Select **Added to the site homepage** or **Added to a site page** to automatically add the widget when Wix users install your app for the first time. * **Show this widget in the Add Elements panel**: Turn on this option so the updated widget appears in the **Add Elements** panel. **For plugins:** * **Plugin name**: Ensure the plugin name doesn't include "Old" or "Previous" so it appears as a viable option in the plugin explorer. * **Add this plugin automatically**: Turn on this option and select the appropriate slot so the plugin is automatically added when Wix users install your app for the first time. 4. Configure the default preset and any other installation settings as needed. 5. Click **Save**. ## Step 4 | Release so both extension configurations take effect Release your app so the previous extension is hidden for new installs and the updated extension is available. When available, release as a minor version so that Wix users on the current major version immediately see the updated extension in the **Add Elements** panel for widgets or in the plugin explorer for plugins, and receive the update automatically. 1. If your app includes extensions built with Blocks, you need to release in the Blocks editor so those extension configurations take effect. See [Manage Blocks App Versions](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/deploy-and-manage-blocks-apps/manage-blocks-app-versions.md). 2. If your app includes extensions built with other frameworks, release using whichever method matches your app's framework: * **Self-managed:** Follow the steps in [Release a New App Version](https://dev.wix.com/docs/build-apps/manage-your-app/versioning/release-a-new-app-version.md). * **Wix CLI:** Run the [`wix release`](https://dev.wix.com/docs/wix-cli/command-reference/project-commands/release.md) command. > __Note:__ If **Minor version** isn't available or appropriate for your changes, you may need to release a **Major version**. However, major updates require Wix user action to install, so use the major version option only when necessary. ## Special considerations ### For legacy custom element widgets and iframes Legacy custom element widgets and iframe components don't show in the **Add Elements** panel by default. To mark these as not added to the site, [contact Wix support](https://www.wix.com/support-chatbot?nodeId=25a57397-ccf7-4376-8b74-48d51edf7159&referral=devRels) for assistance. ## See also - [About site widget extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) - [About app versioning](https://dev.wix.com/docs/build-apps/manage-your-app/versioning/about-app-versioning.md) - [Release a new app version](https://dev.wix.com/docs/build-apps/manage-your-app/versioning/release-a-new-app-version.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Create a Settings Panel for a Site Widget or Plugin (Wix CLI and Self-Hosting) ## Article: Create a Settings Panel for a Site Widget or Plugin (Wix CLI and Self-Hosting) ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/create-a-settings-panel-for-a-site-widget-or-plugin-wix-cli-and-self-hosting.md ## Article Content: # Create a Settings Panel for a Site Widget or Plugin When creating a [self-managed site widget](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-hosted-site-widget-extensions-with-custom-elements.md), a [self-managed site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/develop-a-self-managed-app/supported-extensions/site-extensions/site-widgets-and-plugins/add-site-plugin-extensions-with-custom-elements.md), a [site widget built with the CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/about-site-extensions.md#custom-element-site-widget-extensions), or a [site plugin built with the CLI for Apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/supported-extensions/site-extensions/site-plugins/add-a-site-plugin-extension-in-the-cli.md), you also need to create a settings panel, enabling users to customize the widget or plugin in the editor. The settings panel is displayed when users click the **Settings** button in the widget or plugin’s action bar. It's rendered as an iframe, giving you the flexibility to build it using any tools or frameworks you choose. > **Note**: For site widgets created with Wix Blocks, you can build [custom panels](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/editor-experience-panels/about-panels-in-blocks.md) directly within the Blocks editor. ## Design the settings panel When designing your settings panel's UI, ensure it is intuitive and user-friendly. Consider using the [Wix Design System](https://dev.wix.com/docs/build-apps/develop-your-app/design/about-the-wix-design-system.md) to maintain a cohesive design. Use components such as: * [`SidePanel`](https://www.wix-pages.com/wix-design-system/?path=/story/components-overlays--sidepanel#Settings_panel) to create a structured layout. * [`FormField`](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--formfield) and input components to collect user input effectively. These tools help you design a panel that aligns with the overall Wix editor experience. ## Code the settings panel In the panel’s code, use Wix's [JavaScript SDK](https://dev.wix.com/docs/sdk.md) to [access widget properties](https://dev.wix.com/docs/sdk/host-modules/editor/widget/introduction.md) and [retrieve environmental data from the editor](https://dev.wix.com/docs/sdk/host-modules/editor/info/introduction.md), as well as access and manage Wix business solutions. To apply changes made in the settings panel to the widget, use the Widget API’s [`setProp()`](https://dev.wix.com/docs/sdk/host-modules/editor/widget/set-prop.md) function. Widget properties are bound to your custom element’s attributes, so any change in the properties automatically updates the corresponding attribute. To handle attribute updates so they are reflected in your widget in the editor, use the [`attributeChangedCallback()`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes) in your custom element's code. Here is a sample code snippet showing how to update a custom element’s attribute: ```js import { editor, widget } from '@wix/editor'; import { createClient } from '@wix/sdk'; const client = createClient({ host: editor.host(), modules: { widget } }); // Get the value of the settings panel's input control const id = 'color_input'; const element = document.getElementById(id); const val = element.value; // Update the custom element's 'color' attribute client.widget.setProp('color', val); ``` ## Integrate Wix's native color and font pickers Enhance the user experience by integrating Wix's native color and font pickers. These tools allow users to select from their site’s theme colors and fonts, ensuring consistency with the site's design. Learn more about [using Wix's color and font pickers](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/integrate-wix-s-native-color-and-font-pickers-in-a-site-widget-s-settings-panel.md). ## Connect widget colors and fonts to a site theme Wix sites expose theme styles as CSS variables, which include tokens for colors and fonts. These variables enable your widget to automatically adopt the site’s theme, ensuring seamless visual integration. Learn more about [connecting colors and fonts to a site theme](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/connect-a-custom-element-s-colors-and-fonts-to-a-site-theme.md). ## Identify your widget's app instance An [app instance](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances.md) represents a specific installation of your app on a Wix site, identified by a unique `instanceId`. You may need the instance ID for various purposes, such as: * **Storing Site-Specific Data**: Use the `instanceId` as a foreign key to save data like user preferences or analytics. * **Configuring Settings**: Apply site-specific configurations such as design settings and integration preferences. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Custom Elements vs. Editor React Components ## Article: Choose Custom Elements or Editor React Components for Your App ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/custom-elements-vs-editor-react-components.md ## Article Content: # Custom Elements vs. Editor React Components [Custom elements](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/custom-elements/add-a-custom-element-extension.md) and [Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) are two ways to build interactive components that Wix users can add to their sites. Custom elements work across all Wix editors, while Editor React Components are designed for Wix Harmony, Wix's AI-powered editor. ## Comparison at a glance | | Custom Elements | Editor React Components | |---|---|---| | **Editor support** | Wix Studio, Wix Editor, Wix Harmony | Wix Harmony | | **Panels & controls** | You must build them from scratch | Automatically generated from the component manifest | | **AI-first features** | No | Yes | ## Custom Elements Custom elements use the web components standard to build interactive components with custom HTML elements. Accompanying settings panels must be built manually. Custom elements are available on Wix Studio, Wix Editor, and Wix Harmony. Best for apps that need to reach users across all Wix editors. Learn more about [building with custom elements](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/custom-elements/add-a-custom-element-extension.md). ## Editor React Components Editor React Components are an AI-first feature available exclusively to developers [building apps for Wix Harmony](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md), Wix's new AI-powered site editor. Harmony is built on a technical architecture that's different from previous editors, and Editor React Components are designed to take full advantage of it. Editor React Components use the React framework to build components that give third-party developers the same panels, controls, and capabilities available to internal Wix developers. Settings panels and controls are automatically generated from the component manifest, meaning full parity with native Wix components, easier theme connections, and the ability to create highly customized experiences with standard React and CSS patterns. Learn more about [building with Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md). ## See also - [About Site Extensions in the Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/about-site-extensions.md) - [Create a Settings Panel for a Custom Element](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/create-a-settings-panel-for-a-site-widget-or-plugin-wix-cli-and-self-hosting.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Page Extensions ## Article: About Site Page Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md ## Article Content: # About Site Page Extensions A site page extension adds a full page to the user's site. The page can appear in the site's main navigation menu and behaves just like any other page. Learn more about [how site owners manage pages](https://support.wix.com/en/article/wix-editor-managing-your-sites-pages). Your app's site pages are structured as a series of [sections](https://support.wix.com/en/article/wix-editor-adding-and-setting-up-sections), each containing a [site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md). A page can contain as many widgets as you like. This means that before setting up a page, your app must have at least one widget. ![site page app](https://wixmp-833713b177cebf373f611808.wixmp.com/images/63788024676d56b25d96513bea0e5d01.png) You can add multiple pages to your app. You must assign a unique ID to each page you add, which is used when referring to the page in code. When your app is installed on a site, the ID is also used as the page's default [URL slug](https://support.wix.com/en/article/wix-editor-changing-your-page-url). When setting up the page extension, you can determine whether site owners are able to duplicate the page and whether to add it automatically to the site menu after installation. > **Note**: Your app can automatically add a page only to a [regular site menu](https://support.wix.com/en/article/wix-editor-adding-a-site-menu) in the Wix Editor. It cannot add a page to [advanced menus](https://support.wix.com/en/article/wix-editor-adding-and-setting-up-advanced-menus) or to [menus in Wix Studio](https://support.wix.com/en/article/studio-editor-designing-menus). ## Implementation options Once you've created your [site widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md), setting up a site page extension only requires configuration in your app's dashboard, with no coding involved. Add a site page extension to your app by [setting it up in your app's dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/add-a-site-page-extension.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add a Site Page Extension ## Article: Add a Site Page Extension ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/add-a-site-page-extension.md ## Article Content: # Add a Site Page Extension A [site page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md) adds a full page to the user's site.
__Important:__ A site page is structured as a series of [sections](https://support.wix.com/en/article/wix-editor-adding-and-setting-up-sections), each containing a [site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md). This means that before setting up a page, your app must already have at least one widget.
To create a site page extension: 1. In the [Custom Apps page](https://manage.wix.com/account/custom-apps), select an existing app that contains site widgets. 2. In the left menu, select **Develop > Extensions**. 3. Click **Create Extension** and select **Site Page**. 4. Configure the page’s settings: * **Page name**: This name will appear in the site's Pages panel. * **Page ID**: The ID will be used to refer to the page in code, and will also become the default URL slug. * Select whether to prevent users from duplicating the page. * Select whether to add the page automatically to the site menu after installation. > **Note**: Your app can automatically add a page only to a [regular site menu](https://support.wix.com/en/article/wix-editor-adding-a-site-menu) in the Wix Editor. It cannot add a page to [advanced menus](https://support.wix.com/en/article/wix-editor-adding-and-setting-up-advanced-menus) or to [menus in Wix Studio](https://support.wix.com/en/article/studio-editor-designing-menus). 5. To define which widgets to include in the page, click **+ Add Widgets** and select any of your app’s widgets. Each widget will be added to the page in its own section in the order that you defined. > **Note**: If you include a widget that is set as [essential to your app's functionality](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md#essential-widgets), deleting the page from a site will delete the entire app. 6. Click **Save**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Lightbox Extensions ## Article: About Site Lightbox Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-lightboxes/about-site-lightbox-extensions.md ## Article Content: # About Site Popup Extensions > **Note:** The terms popup and lightbox refer to the same element. While the editor and dashboard now refer to it as a [popup](https://support.wix.com/en/article/studio-editor-using-popups), the API methods continue to use the term lightbox for backward compatibility. The documentation uses both terms accordingly. A site popup extension adds a popup to the user's site, enabling your app to grab site visitors' attention and showcase important announcements or information. The popup can also appear in the site's navigation menu and behaves like any other popup. You can add multiple popups to your app. Learn more about [how site owners manage popups](https://support.wix.com/en/article/studio-editor-using-popups). The content of a popup extension is composed of one of your app's [site widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md). This means that before setting up a site popup extension, your app must have at least one widget. The popup content is surrounded by a background overlay, which blocks out the site page so that visitors can't click or interact with the site. This helps them focus on the popup. The following example shows a site popup: ![popup](https://wixmp-833713b177cebf373f611808.wixmp.com/images/25c16cad8c70a620ec33322300558d43.png) When setting up the popup extension, you can configure the default methods for site visitors to close the popup. The following options are available: * Clicking the overlay background behind the popup. * Clicking an 'X' button. When selected, an 'X' button is added to the top right corner of the popup. When testing your popup, make sure that the 'X' does not interfere with your design, as shown in the following image: ![popup](https://wixmp-833713b177cebf373f611808.wixmp.com/images/01fb7e8f3f625877bb3b56927bd5c52c.png) ## Implementation options Once you've created your [site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md), setting up a site popup extension only requires configuration in your app's dashboard, with no coding involved. Add a site popup extension to your app by [setting it up in your app's dashboard](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md). ## Open and close a popup from your app's code Your app can trigger the popup from code in its other app extensions, such as a site widget or an embedded script. To do this, use the [`openLightbox()`](https://dev.wix.com/docs/sdk/frontend-modules/window/open-lightbox.md) method. This method requires the unique extension ID of the popup. You can obtain the extension ID by clicking **Copy extension ID** in the ![more-actions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b8863ab2de70563b18ead3fb12c778dc.png) More Actions menu of your popup extensions in the **Extensions** page of you app dashboard. To close the popup from its own code, use the [`lightbox.close()`](https://dev.wix.com/docs/sdk/frontend-modules/window/lightbox/close.md) method. For example, you can add a button inside the popup that calls the `close()` method and passes data back to the code that initially opened it. ### Code example The following example demonstrates how to use a popup to confirm user details before submission: * The widget collects user details and opens a popup for confirmation. * The popup displays the details and provides a **Confirm** button. * When the user clicks **Confirm**, the popup closes and sends a confirmation response to the widget. * If the user cancels, the widget receives a cancellation response. **Widget code (opens a popup and sends user data)** ```js import { lightbox } from '@wix/site-window'; // Function triggered when the "Open" button is clicked export function openButton_click(event) { // Prepare user data to send to the lightbox (popup) const userData = { name: "John Doe", email: "johndoe@example.com" }; // Open the lightbox (popup) and pass user data lightbox.open({ extensionId: "52e53dff-27eb-4b5f-b6b8-4956bcadaea1", data: { userData } }) .then((data) => { if (data?.confirmed) { console.log("User confirmed details. Proceeding with submission..."); // Submit the form or take further action } else { console.log("User canceled confirmation."); } }); } ``` **Popup code (displays and returns the confirmation status)** ```js import { lightbox } from '@wix/site-window'; // Retrieve user data passed from the widget const { userData } = lightbox.getContext() || {}; console.log("Received user data:", userData); // Function triggered when the "Confirm" button is clicked export function confirmButton_click(event) { // Close the lightbox (popup) and return a confirmation response lightbox.close({ confirmed: true }); } // Function triggered when the "Cancel" button is clicked export function cancelButton_click(event) { // Close the lightbox (popup) and return a cancellation response lightbox.close({ confirmed: false }); } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Add a Site Lightbox Extension ## Article: Add a Site Lightbox Extension ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-lightboxes/add-a-site-lightbox-extension.md ## Article Content: # Add a Site Popup Extension A [site popup extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md) adds a pop-up to the user's site.
**Important:** The content of a popup extension is composed of one of your app's [site widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md). This means that before setting up a popup, your app must already have at least one widget.
To create a site popup extension: 1. In the [Custom Apps page](https://manage.wix.com/account/custom-apps), select an existing app that contains site widgets. 2. In the left menu, select **Extensions**. 3. Click **Create Extension** and select **Site Popup**. 4. Enter a name for the popup. This name will appear in the site's Pages panel. 5. Configure the options for closing the popup: * Select whether site visitors can close it by clicking the overlay background behind the popup. * Select whether site visitors can close it by clicking an 'X' button. When selected, an 'X' button is added to the top right corner of the popup. When testing your popup, make sure that the 'X' does not interfere with your design. > **Note**: If you haven't set up a way to [close the popup in its code](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/about-site-extensions.md#open-and-close-a-popup-from-your-app's-code), we recommend selecting at least one of these options. 6. To define the widget that will appear in the popup, click **+ Add Widget** and select any of your app's widgets. > **Note**: If you include a widget that is set as [essential to your app's functionality](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md#essential-widgets), deleting the popup from a site will delete the entire app. 7. Click **Save**. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Widget Extensions ## Article: About Site Widget Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md ## Article Content: # About Site Widget Extensions Site widgets are draggable UI components that Wix site owners can add to pages on their website. They enhance a site's functionality by displaying content or enabling site visitors to perform various tasks. When working in the site editor, site owners can adjust the size of the widget, reposition it, and customize it. ## Implementation options You can add a site widget extension using: * [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-widgets/build-a-site-widget-in-blocks.md): Create a site widget in Wix's native app editor. Design your widget using visual layout and design tools, and code your business logic using Velo, Wix's native coding solution. * [Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/custom-elements/add-a-custom-element-extension-with-the-wix-cli.md): Create a site widget using the CLI, with your code by default deployed on our servers. The creation and setup process of your widget takes place in the terminal. Then, to edit your widget, you write code directly in your CLI app project's files. * [Self-managed custom element](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-hosted-site-widget-extensions-with-custom-elements.md): Create a site widget using custom element technology, with your code deployed on your own server. The custom element is essentially a new HTML tag that you define, which is made available in the Wix editors as a widget. * [Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md): An AI-first feature available exclusively on Wix Harmony. Build site widgets using the React framework with the same panels, controls, and capabilities available to internal Wix developers. ### Choosing your implementation Here are some factors to consider when choosing which implementation to work with: * Site widgets currently can't be pinned to a specific location on a page. If your app requires this, best to use an [embedded script component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md). * Site widgets built with Wix Blocks support all the functionality available in [Wix's editor elements](https://support.wix.com/en/article/studio-editor-adding-elements-4240855#types-of-elements). If your extension should include functionality that isn't supported, use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-widgets/add-a-site-widget-extension.md) or a [self-managed custom element](https://dev.wix.com/docs/build-apps/develop-your-app/develop-a-self-managed-app/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-managed-site-widget-extensions-with-custom-elements.md). * Site widgets built with [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-widgets/build-a-site-widget-in-blocks.md) can connect to a site's color and typography themes, and support advanced capabilities for configuring the widget customization experience in the editor. * Editor React Components are only supported on Wix Harmony. * For a detailed comparison of custom elements and Editor React Components, see [Custom Elements vs. Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/custom-elements-vs-editor-react-components.md).
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
## Interact with a user site The way to interact with a user site from your site widget extension depends on your implementation. For widgets created with Blocks, use [Velo](https://dev.wix.com/docs/velo.md). For widgets created via the CLI, use the [Site API](https://dev.wix.com/docs/sdk/host-modules/site/introduction.md). > **Note:** Most widget types can't call Wix APIs while being previewed in the site editor. Editor React Components are the exception and can call platformized APIs in the site editor. ## Essential widgets An essential widget is crucial to your app's functionality. You can [set a widget to be essential](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/add-a-site-page-extension.md) only if the widget is added as part of a [site page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md). Deleting an essential widget from a site or deleting the section or page that contains it deletes the entire app. Wix users can add an essential widget to a site only once and can't duplicate it. > **Note**: Essential widgets are not available for Editor React Components. ## Sandboxing in the editor > **Note**: Essential widgets are not available for Editor React Components. You can set a widget to be essential only if the widget is added as part of a [site page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-pages/about-site-page-extensions.md).
__Important:__ The format of the Wix editor URL is changing due to new cookies introduced for authorization. The new URL format is `{username}-{sitename}.{editor|studio|harmony}.wix.com`. If your app uses CORS and whitelists specific origins, update your allowed origins to match the new format to avoid blocked requests.
Learn more about [handling sandboxing in the editor](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/handle-sandboxing-in-the-editor.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Display a Site Widget in the Add Panel and Site Dashboard ## Article: Display a Custom Element Site Widget in the Add Elements Panel ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/display-a-site-widget-in-the-add-panel-and-site-dashboard.md ## Article Content: # Display a Site Widget in the Add Panel and Site Dashboard You can display your [site widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md) in the following areas: * The **Add Elements** panel in the site editor. * The **Manage Apps** page in the site dashboard. > **Note**: [Essential widgets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/about-site-widget-extensions.md#essential-widgets) are automatically added to a site when an app is installed and can only appear once, so they don't appear in the **Add Elements** panel or the **Manage Apps** page. When creating your site widget, configure the following content: * A short description of the widget's functionality, which appears in an info tooltip. * A preview image showcasing the widget's appearance. Learn more about creating a custom element site widget: * [Add a Custom Element Site Widget Extension in the CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-widgets/add-a-site-widget-extension.md) * [Add Self-Managed Site Widget Extensions with Custom Elements](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-hosted-site-widget-extensions-with-custom-elements.md#add-self-hosted-site-widget-extensions-with-custom-elements) ## Add Elements panel Your widget appears in the panel's **App Widgets** section. Wix users can find it there and add it to any page on a site. The following image shows how a widget appears in the panel: ![widget-add-panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8db1263882dbd1393c1f2e73ea87353a.png) ## Manage Apps page Your widget appears in the **Manage Apps** page of the site dashboard. Wix users can find it listed under your app and add it to any page on a site. The following image shows how a widget appears in the Manage Apps page: ![widget-manage-apps](https://wixmp-833713b177cebf373f611808.wixmp.com/images/66f8e70e5f97754325e6b77a139d4167.png) ## Guidelines for creating a preview image The preview image you provide appears in both the **Add Elements** panel and the **Manage Apps** page. When creating your preview image, keep the following content and technical guidelines in mind. ### Content guidelines * The image should be a visual representation of the preset. It's not a banner or an ad. * If your widget displays dynamic content, include dummy content but avoid Lorem Ipsum. ### Technical guidelines * Use a screenshot of the widget to capture its actual dimensions and original pixel size. * Save at the highest quality and avoid compression. * Supported formats: JPG or PNG --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Integrate Wix's Native Color and Font Pickers in a Site Widget's Settings Panel ## Article: Allow Users to Choose Colors and Fonts in Wix's Editors ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/integrate-wix-s-native-color-and-font-pickers-in-a-site-widget-s-settings-panel.md ## Article Content: # Integrate Wix's Native Color and Font Pickers in a Site Widget's Settings Panel When creating a settings panel for a [self-managed site widget](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/site-widgets-and-plugins/add-self-hosted-site-widget-extensions-with-custom-elements.md) or a [site widget built with the CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-widgets/add-a-site-widget-extension.md), you can integrate Wix's native color and font pickers. These tools also allow users to select from their site's theme colors and fonts, ensuring seamless alignment with the site's design. If a user selects a theme color or font, your widget's style will automatically update whenever users modify their site's theme settings. ## Integrate the color picker in your settings panel Wix's native color picker allows users to easily select colors from their site's theme colors or pick a custom color. This can be especially useful for widgets that need to maintain design consistency with the rest of the site. ![color-picker](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d74f41e45115e27d327c26d4746f2917.png) Use the Inputs API's [`selectColor()`](https://dev.wix.com/docs/sdk/host-modules/editor/inputs/select-color.md) function to open the color picker panel in the editor, prompting the user to select a color. > **Note**: If a user selects a theme color, the returned value contains a CSS variable function. This function dynamically retrieves the value of the corresponding [site theme CSS variable](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/site-theme-css-variables.md). For example, it might return a value like `'var(--wst-color-line, #000000)'`. The following example shows how to create a settings panel that allows users to select and update a background color using the Wix color picker, with the selected color being saved and retrieved using `widget.getProp` and `widget.setProp`. ```js import React, { useState, useEffect, FC } from 'react'; import { FormField, FillPreview, SidePanel, Box, WixDesignSystemProvider } from '@wix/design-system'; import { widget, inputs } from '@wix/editor'; const Panel: FC = () => { const [backgroundColor, setBackgroundColor] = useState(''); // Initialize state for background color // Use useEffect to retrieve the background color when the component mounts useEffect(() => { const fetchBackgroundColor = async () => { const color = await widget.getProp('background-color'); // Retrieve the custom element's background-color attribute setBackgroundColor(color || ''); // Set the background color if available }; fetchBackgroundColor(); }, []); // Only run once when the component mounts // Handle color change after selecting a new color from the picker const handleColorChange = (newColor: string) => { setBackgroundColor(newColor); // Update local state for backgroundColor widget.setProp('background-color', newColor); // Set the new backgroundColor in the widget }; return ( {/* Color preview that opens a color picker */} inputs.selectColor(backgroundColor, { onChange: (value: string) => { handleColorChange(value ?? ''); // Handle color change }, }) } /> ); }; export default Panel; ``` ## Integrate the font picker in your settings panel Wix's native font picker allows users to select fonts directly from their site's theme styles or choose custom fonts. This ensures that your widget's typography remains consistent with the rest of the site. ![font-picker](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4bf1e9a2d529b2d451ff4fadcad8e392.png) Use the Inputs API's [`selectFont()`](https://dev.wix.com/docs/sdk/host-modules/editor/inputs/select-font.md) function to open the font picker panel in the editor, prompting the user to select a font. The font picker returns an object containing the user's selected font details, including the `font` and optionally the `textDecoration` property. The value of the `font` property is a [`font` CSS shorthand property](https://developer.mozilla.org/en-US/docs/Web/CSS/font), which can include the following: * Font family * Font size * Font weight * Font style (supports `italic` only) > **Note**: If a user selects a theme font style, the returned `font` property contains a CSS variable function. This function dynamically retrieves the value of the corresponding [site theme CSS variable](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/site-theme-css-variables.md). For example, it might return a value like `'var(--wst-font-style-h2)'`. The value of the `textDecoration` property is a [`text-decoration` CSS shorthand property](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration). It can also be set to `undefined`. The following example shows how to create a settings panel that allows users to select and update a title font using the Wix font picker, with the selected font being saved and retrieved using `widget.getProp` and `widget.setProp`. ```js import React, { useState, useEffect, FC } from 'react'; import { FormField, SidePanel, Button, WixDesignSystemProvider } from '@wix/design-system'; import { widget, inputs } from '@wix/editor'; const Panel: FC = () => { const [selectedFont, setSelectedFont] = useState(''); // Initialize state for selected font string // Use useEffect to retrieve the selected font when the component mounts useEffect(() => { const fetchFont = async () => { const font = await widget.getProp('title-font'); // Retrieve the custom element's title-font property setSelectedFont(font || ''); }; fetchFont(); }, []); // Handle font change after selecting a new font from the picker const handleFontChange = (newFont: any) => { const fontString = newFont?.font || ''; // Extract the font string setSelectedFont(fontString); widget.setProp('title-font', fontString); }; return ( ); }; export default Panel; ``` ### Load the fonts used within your custom element When developing custom elements, the page's stylesheet is not aware of the fonts used within the custom element. To ensure that the custom element has access to the required font styles, call [`setPreloadFonts()`](https://dev.wix.com/docs/sdk/host-modules/editor/widget/set-preload-fonts.md) from your [settings panel](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/create-a-settings-panel-for-a-site-widget-or-plugin-wix-cli-and-self-hosting.md). This function sets the list of fonts that are preloaded when the custom element is displayed on a site. > **Note**: Calling `setPreloadFonts()` overwrites any fonts that were previously set to be preloaded. This means that any fonts not included in the current call will no longer be preloaded. Make sure to pass all the fonts that need to be preloaded every time you call the function. ### Load fonts in a shadow DOM or internal iframe When working with custom elements that use a shadow DOM or an internal iframe, the page's stylesheet may be inaccessible. To ensure your custom element has access to the required font styles, call [`getFontsHtml()`](https://dev.wix.com/docs/sdk/frontend-modules/site/introduction.md) to retrieve the necessary CSS and load the fonts. The following example shows how to create a custom element with a shadow DOM that dynamically reads font configuration attributes and applies the specified styles to its content, using `getFontsHtml()` to load the necessary font CSS: ```js import { site } from '@wix/site-site'; // Import the site module for font handling // Define the custom element class FontsCustomElement extends HTMLElement { constructor() { super(); // Attach a shadow root to the element this.attachShadow({ mode: 'open' }); } connectedCallback() { // When the custom element is added to the DOM, it will read the initial attributes this.updateFonts(); } attributeChangedCallback(name, oldValue, newValue) { // This will be triggered when any attribute changes if (name === 'title-font' || name === 'body-font') { this.updateFonts(); } } static get observedAttributes() { // Define which attributes to watch for changes return ['title-font', 'body-font']; } async updateFonts() { // Dynamically read the attributes const titleFont = this.getAttribute('title-font'); const bodyFont = this.getAttribute('body-font'); // Fetch the HTML for the provided fonts dynamically const fontHtml = await site.getFontsHtml( [titleFont, bodyFont].filter(Boolean) // Only include non-empty fonts ); // Set the shadow DOM's innerHTML with the font content this.shadowRoot.innerHTML = `
${fontHtml}
This is the Title
This is the body text
`; } } // Register the custom element with the browser customElements.define('fonts-custom-element', FontsCustomElement); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Connect a Custom Element's Colors and Fonts to a Site Theme ## Article: Connect a Custom Element's Colors and Fonts to a Site Theme ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/connect-a-custom-element-s-colors-and-fonts-to-a-site-theme.md ## Article Content: # Connect a Custom Element's Colors and Fonts to a Site Theme When site owners design a Wix site, linking elements to the site's theme ensures a clean, cohesive, and professional design. A color theme provides a predefined set of colors, while a typography theme defines the appearance of site text. Learn more about [site themes in Wix Studio](https://support.wix.com/en/article/studio-editor-about-site-styles) and [site themes in Wix Editor](https://support.wix.com/en/article/wix-editor-customizing-your-sites-theme-and-design). If you're building a site widget with a custom element, you can configure it to automatically adopt the site's theme styles. This ensures your widget integrates seamlessly into the site's design, maintaining consistency across all components. ## Access theme styles with CSS variables Every site's theme styles are exposed as CSS variables on the web page, making it easy to reference and apply them to your custom elements. These variables include design tokens for the colors and fonts that match the site's theme. For a comprehensive list of available styles, refer to the [site theme CSS variables](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/frontend-code/custom-css/site-theme-css-variables.md). By using CSS variables, your custom element will automatically adapt to changes in the site's theme. This ensures your element remains consistent with the site's updated design without requiring manual intervention. ## Assign theme styles to custom elements To connect your custom element's styles to the site's theme, resolve the appropriate CSS variables and apply them in your code. The following are examples of integrating theme colors and fonts.
**Tips:** * Test your widget on sites with different themes to ensure it works well across various designs and settings. * Always use fallback values in case a theme variable is undefined. For example: ```js .title { color: var(--wst-color-text-primary, #000000); } ```
### Connect to a theme color Assign a theme's primary background color to your custom element: ```js .container { background-color: var(--wst-color-fill-background-primary); ``` Replace `--wst-color-fill-background-primary` with any other theme color variable depending on your design requirements. ### Connect to a theme font Apply a theme's font style to a text element: ```js .title { font: var(--wst-font-style-h2); ``` Ensure you use the correct CSS variable for the specific font style, such as `--wst-font-title`,`--wst-font-paragraph`, or others, depending on the text's purpose. ### Embed site theme styles in a shadow DOM or internal iframe Normally, the theme stylesheet is automatically included with the webpage, ensuring consistent styling across the site. However, when working with custom elements that use a shadow DOM or an internal iframe, the page's stylesheet may be inaccessible. When working with custom elements in Wix, you might encounter situations where the page's main stylesheet is inaccessible. In such cases, you can use [`getSiteThemeHtml()`](https://dev.wix.com/docs/sdk/frontend-modules/site/introduction.md) to dynamically embed the site's theme styles directly into your custom element, including colors and fonts. The following example shows how to use `getSiteThemeHtml()` to dynamically apply your Wix site's theme styles to a custom element's shadow DOM: ```js import { site } from '@wix/site-site'; // Import the site module for theme handling class ThemedCustomElement extends HTMLElement { constructor() { super(); } connectedCallback() { this.loadSiteTheme(); } async loadSiteTheme() { // Fetch the site's theme HTML const themeHtml = await site.getSiteThemeHtml(); // Attach the shadow DOM const shadow = this.attachShadow({ mode: 'open' }); // Embed the theme CSS and content directly into the shadow root shadow.innerHTML = ` ${themeHtml}
This is a themed element with site styles applied.
`; } } // Register the custom element customElements.define('themed-custom-element', ThemedCustomElement); ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Site Plugin Extensions ## Article: About Site Plugin Extensions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md ## Article Content: # About Site Plugin Extensions With site plugins, you can create interactive and feature-rich components that seamlessly [integrate into Wix’s business solutions](https://dev.wix.com/docs/build-apps/get-started/overview/integrating-with-wix-s-business-solutions.md), such as Wix Stores, Wix Bookings, and Checkout & Orders, extending their functionality and user experience. Wix users can easily place site plugins into predefined [slots](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md) within Wix apps, using the plugin explorer available in all Wix editors. ![plugin-flow](https://wixmp-833713b177cebf373f611808.wixmp.com/images/71c2ec2dcfeeff1db782f291c2441d39.gif) ## Terminology The following are key terms related to site plugins: | Term | Definition | |-----|----| | Host widget | A widget belonging to a Wix business solution, which contains 1 or more slots. | | [Slot](#slots) | A placeholder within a host widget in which Wix users can add a plugin. | | Site plugin | A component you can add inside a slot, extending the host widget's functionality. | | [Plugin API](#plugin-api) | The data each host widget exposes to your plugin at runtime. | ## How site plugins work A site plugin integrates with its host widget in 2 ways: * **Visually**, by embedding its UI inside one of the host's slots. * **Logically**, by implementing a communication interface with the host through their [plugin API](#plugin-api). ## Slots [Slots](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md) are UI placeholders where you can place plugins. Wix apps offer a range of slots across different app widgets. When developing a plugin, you must declare the specific slots Wix users can add a plugin to. Each host widget passes context data to your plugin as props, which you can use to call [Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md), implement your plugin's logic, and extend a host widget's functionality. For a list of available slots, see [About Slots](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md). ## Plugin API Each slot exposes a plugin API, which is a set of context data passed to your plugin at runtime as props. For example, the Product page plugin API provides `productId`, `selectedVariantId`, and other data about the current product. Use these props to call related [Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md) and implement your plugin's logic. ## Implementation options You can add a site plugin extension using: * [Wix CLI for Apps](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/supported-extensions/site-extensions/site-plugins/add-a-site-plugin-extension-in-the-cli.md): Create a site plugin using the CLI and custom element technology, with your code by default deployed on our servers. The creation and setup process of your widget takes place in the terminal. Then, to edit your plugin, you write code directly in your CLI app project's files. * [Self-managed custom element](https://dev.wix.com/docs/build-apps/develop-your-app/develop-a-self-managed-app/supported-extensions/site-extensions/site-widgets-and-plugins/add-site-plugin-extensions-with-custom-elements.md): Create a site plugin using custom element technology, with your code deployed on your own server. The custom element is essentially a new HTML tag that you define, which is made available in the Wix editors as a widget. * [Wix Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/site-plugins/build-a-site-plugin-in-blocks.md): Create a site widget in Wix's native app editor. Design your site plugin using visual layout and design tools, and code your business logic using Velo, Wix’s native coding solution.
**Editor compatibility** Wix Blocks apps aren't supported in the Wix Harmony editor. Existing Blocks apps remain available for purchase on the Wix App Market for Wix Editor and Wix Studio sites. To learn more, see [About Wix Harmony and Blocks](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-blocks/about-wix-harmony-and-blocks.md).
## Design and layout guidelines Some host widgets include design and UX guidelines for building plugins that integrate with the host widget's layout and Wix's design standards. Follow these guidelines to ensure your plugin appears native to the host widget. ## Sandboxing in the editor Site plugins are sandboxed when rendered in the editor to enhance security. This means they're treated as if they come from a different domain, which impacts access to browser storage APIs and other same-origin resources.
__Important:__ The format of the Wix editor URL is changing due to new cookies introduced for authorization. The new URL format is `{username}-{sitename}.{editor|studio|harmony}.wix.com`. If your app uses CORS and whitelists specific origins, update your allowed origins to match the new format to avoid blocked requests.
Learn more about [handling sandboxing in the editor](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/handle-sandboxing-in-the-editor.md). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Build a Dashboard Page to Manage Your Site Plugin ## Article: Build a Dashboard Page to Manage Your Site Plugin ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md ## Article Content: # Build a Dashboard Page to Manage Your Site Plugin When building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md), you may want to provide users with a back office management interface for the plugin. You can create one either within the Wix dashboard, or as an external dashboard on a third-party platform. [Learn about building a dashboard page extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/about-dashboard-extensions.md) ## Add your plugin to a slot from a dashboard page To let users add your plugin to their site through your app's [dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/dashboard-extensions/dashboard-pages/about-dashboard-page-extensions.md), create a user interface that triggers the Wix Dashboard API's `addSitePlugin()` function. Specify the desired slot when calling this function. This initiates a user flow where Wix requests the user's consent to add the plugin. If the selected slot is already occupied, the user can choose to replace the existing plugin with your plugin. Plugins for the checkout page must implement this functionality to enable users to add the plugin to their site. Depending on the framework you're using to develop your dashboard page, use one of the following APIs: * [Velo in Blocks: `addSitePlugin()`](https://dev.wix.com/docs/velo/api-reference/wix-dashboard/add-site-plugin.md). * [Wix SDK: `addSitePlugin()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md). The `addSitePlugin()` function takes the following parameters: * `pluginId`: ID of your site plugin, which you can find in your app's site plugin extension. To view the extension, go to your app's dashboard in the [Wix Studio workspace](https://manage.wix.com/studio/custom-apps). * `placement`: Details of the slot in which you want to add the plugin, including the hosting app's `appDefinitionId`, and the relevant `widgetId` and `slotId`. Learn more about the locations and identifiers of [slots that are available on Wix app pages](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md). The following example shows how to use Velo in Blocks to add a site plugin to the checkout page when a user clicks an **Add Plugin** button in the dashboard: ```js import { addSitePlugin } from 'wix-dashboard'; $w.onReady(function() { $w('#addPluginBtn').onClick(() => { const pluginId = '9fbd5efb-1b58-489e-b896-47f6851603f0'; const pluginPlacement = { appDefinitionId: "1380b703-ce81-ff05-f115-39571d94dfcd", widgetId: "14fd5970-8072-c276-1246-058b79e70c1a", slotId: "checkout:summary:before" } addSitePlugin(pluginId, { placement: pluginPlacement }) .then(() => { console.log('Plugin added successfully'); }) .catch(error => { console.error('Error adding plugin:', error); }); }) }); ``` ## Check your plugin's placement status You can check whether your plugin is currently placed in a slot on a specific site. Depending on the framework you're using to develop your dashboard page, use one of the following APIs: * [REST: Get Placement Status](https://dev.wix.com/docs/rest/app-management/site-plugins/placement-status-v1/get-placement-status.md) * [Velo in Blocks: `getPlacementStatus()`](https://dev.wix.com/docs/velo/api-reference/wix-site-plugins-v1/plugins/get-placement-status.md) Both APIs return an array of objects indicating whether each of your app's site plugins is currently placed in a slot on the user's site. For example: ```js import { plugins } from "wix-site-plugins.v1"; ... const { placementStatuses } = await plugins.getPlacementStatus(); const myPluginPlacementStatus = placementStatuses[0]; if (myPluginPlacementStatus.placedInSlot) { onPluginPlacedSuccessfully(); } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Slots ## Article: About Slots ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md ## Article Content: # About Slots Wix offers a range of slots across Wix app widgets where you can place [site plugins](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md). The following diagram shows how slots can appear in a host widget: ![Wireframe showing slot positions in a host widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/460c008b9392743ed24384f272b68abf.png "slots wireframe") ## How slots work A host widget can contain multiple slots, and each slot has a unique ID. When building your plugin, you declare the specific slots in which Wix users can place it. Each host widget passes context data to your plugin as props, which you can use to call [Wix APIs](https://dev.wix.com/docs/build-apps/develop-your-app/api-integrations/about-wix-apis.md) and implement your plugin's logic. A Wix user can place your plugin in a slot manually, or your plugin can be placed automatically on installation. If you configure automatic placement, your plugin is placed in the first available slot in the order you define. If that slot is occupied, it moves to the next slot, and so on. If no slots are available, it isn't placed. ## Available slots The following host widgets support slots where you can place a site plugin. Learn more about each host widget's slots and integration details. Wix eCommerce: - [Checkout page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-checkout-page.md) - [Side cart](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-side-cart.md) - [Thank You page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md) Wix Stores: - [Product page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md) - [Category page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-category-page.md) - [Shop page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-shop-page.md) - [Gallery widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-gallery-widget.md) Wix Bookings: - [Service page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-bookings/wix-bookings-service-page.md) Wix Events: - [Event Details page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-events/wix-events-event-details-page.md) Wix Blog: - [Post page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-blog/wix-blog-post-page.md) ## See also - [About Site Plugin Extensions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix eCommerce: Checkout Page ## Article: Wix eCommerce: Checkout Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-checkout-page.md ## Article Content: # Wix eCommerce (Checkout & Orders): Checkout Page The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Checkout Page.
Important: Some plugins may not support automatic addition upon installation, even with `autoAddToSite` enabled. In that case, you must: - [Create a dashboard page to manage your site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md) that calls [`addSitePlugin()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md) to let users add the plugin to the checkout slot. - Release at least 1 version of your app so the site plugin extension is registered with Wix for `addSitePlugin()` to work properly.
## Slots The following image shows slots in the checkout page, into which users can add plugins. ![checkout-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c886759054f07416b475304c52fbc31d.png) The slots are represented by the following `placement` object: ```json [ { "appDefinitionId": "", "widgetId": "", "slotId": "" } ] ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "14fd5970-8072-c276-1246-058b79e70c1a" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots).
Supported values:
  • `"checkout:header"`
  • `"checkout:top"`
  • `"checkout:steps:before"`
  • `"checkout:delivery-step:options:after"`
  • `"checkout:policies:after-1"`
  • `"checkout:summary:before"`
  • `"checkout:summary:lineItems:after"`
  • `"checkout:summary:lineItems:after2"`
  • `"checkout:summary:totalsBreakdown:before"`
  • `"checkout:summary:after"`
| For example, for your widget to appear before the totals breakdown in a checkout page use the following object in your configuration: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "14fd5970-8072-c276-1246-058b79e70c1a", "slotId": "checkout:summary:totalsBreakdown:before" } ``` ## Checkout plugin API Use the Checkout plugin API to integrate with the plugin's host. The API provides data about the current checkout process and lets you define a callback function that's invoked whenever changes are made in the checkout. > **Note:** The `checkout:delivery-step:options:after` slot uses a [different API](#delivery-step-options-slot-api). ### Properties | Name | Type | Description | | --------------------- | ------ | ----------- | | `checkoutId` | String | The ID of the current checkout process. | | `stepId` | String | The ID of the step currently rendered in the checkout page, which can be one of the following:
  • `'contact-details'`
  • `'delivery-method'`
  • `'payment-and-billing'`
  • `'place-order'`
  • | | `checkoutUpdatedDate` | String | Date and time the checkout was updated. | ### Functions | Name | Type | Description | | -------- | ---- | ----------- | | `onRefreshCheckout()` | `(refreshCheckoutCallback: () => void) => void` | An event handler that accepts a callback function that's invoked by a widget. The widget should call the function whenever the checkout needs to be refreshed. | ### Code example CLI custom element When building a checkout plugin with the [CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-plugins/add-a-site-plugin-extension.md), the checkout API properties are passed as custom element attributes in kebab-case (for example, `checkoutId` becomes `checkout-id`). ```typescript class MyCheckoutPlugin extends HTMLElement { private refreshCheckoutCallback: (() => void) | null = null; static get observedAttributes() { return ['checkout-id', 'step-id', 'checkout-updated-date']; } connectedCallback() { this.render(); } attributeChangedCallback() { this.render(); } onRefreshCheckout(callback: () => void) { this.refreshCheckoutCallback = callback; } render() { const checkoutId = this.getAttribute('checkout-id'); const stepId = this.getAttribute('step-id'); const checkoutUpdatedDate = this.getAttribute('checkout-updated-date'); this.innerHTML = `

    Checkout ID: ${checkoutId}

    Current step: ${stepId}

    Last updated date: ${checkoutUpdatedDate}

    `; } } export default MyCheckoutPlugin; ``` ### Code example: Velo ```javascript import { checkout } from 'wix-ecom-backend'; // Global variable to use to refresh checkout let refreshCheckoutCallback; $w.onReady(async function () { // Get properties passed by the Checkout page const {checkoutId} = $widget.props; // Property usage const checkout = fetchCheckout(checkoutId); // Add selected items to the checkout on button click $w('#addItems').onClick(async () => { const itemsToAdd = $w('#itemsToChoose').value; await checkout.addToCheckout(checkoutId, {lineItems: itemsToAdd}); // Call refreshCheckoutCallback to refresh the checkout refreshCheckoutCallback(); }) }); // Export function to get the refresh checkout callback /** * @function * @description An event handler that accepts a callback function that's invoked by a widget. The widget should call the function whenever the checkout needs to be refreshed. * @param {string} callback - Callback to be trigger whenever the checkout needs to be refreshed * @returns {void} */ export function onRefreshCheckout(callback){ refreshCheckoutCallback = callback; } ``` ### Dashboard page example Checkout plugins require a [dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md) so users can add the plugin to their checkout page. Use [`addSitePlugin()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md) to trigger the addition flow. The `pluginId` is the ID of your site plugin extension, which you can find in your [app's dashboard](https://manage.wix.com/account/custom-apps) under **Extensions**.
    Important: Some plugins may not support automatic addition upon installation, even with `autoAddToSite` enabled. In that case, you must: - [Create a dashboard page to manage your site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md) that calls [`addSitePlugin()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md) to let users add the plugin to the checkout slot. - Release at least 1 version of your app so the site plugin extension is registered with Wix for `addSitePlugin()` to work properly.
    ```typescript import React, { type FC, useState } from 'react'; import { dashboard } from '@wix/dashboard'; import { Button, Card, Page, Text, Box, WixDesignSystemProvider, } from '@wix/design-system'; import '@wix/design-system/styles.global.css'; const PLUGIN_ID = ''; const PLUGIN_PLACEMENT = { appDefinitionId: '1380b703-ce81-ff05-f115-39571d94dfcd', widgetId: '14fd5970-8072-c276-1246-058b79e70c1a', slotId: 'checkout:summary:after', }; const PluginManagerPage: FC = () => { const [isAdding, setIsAdding] = useState(false); const handleAddPlugin = async () => { setIsAdding(true); try { await dashboard.addSitePlugin(PLUGIN_ID, { placement: PLUGIN_PLACEMENT, }); dashboard.showToast({ message: 'Plugin added to checkout page!', type: 'success', }); } catch (error) { console.error('Failed to add plugin:', error); dashboard.showToast({ message: 'Could not add plugin. It may already be added.', type: 'warning', }); } finally { setIsAdding(false); } }; return ( Add your plugin to the checkout page. ); }; export default PluginManagerPage; ``` ## Delivery step options slot API The `checkout:delivery-step:options:after` slot uses a different API than the other checkout slots. ### Properties | Name | Type | Description | | --------------------- | ------ | ----------- | | `checkoutId` | String | The ID of the current checkout process. | | `checkoutUpdatedDate` | String | Date and time the checkout was updated. | | `selectedDeliveryOptionCarrierId` | String | The ID of the carrier for the selected delivery option. | | `selectedDeliveryOptionId` | String | The ID of the selected delivery option. | | `deliveryStepState` | String | The current state of the delivery step. Possible values: `'open'` or `'summary'`. | ### Functions | Name | Type | Description | | -------- | ---- | ----------- | | `onRefreshCheckout()` | `(callback: () => Promise) => void` | An event handler that accepts a callback function that's invoked by a widget. The widget should call the function whenever the checkout needs to be refreshed. | | `disableContinueButton()` | `(callback: (isDisabled: boolean) => void) => void` | An event handler that accepts a callback function to control the checkout's continue button. Call the callback with `true` to disable the button, or `false` to enable it. | ## Permissions and webhooks The following [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md) are relevant for most checkout plugins: - **Wix Developers > Manage Your App**: Added automatically. - **Stores > Read Stores**: Required for accessing product and store data. The following [webhooks](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md) are relevant to most checkout plugins: - [App Installed](https://dev.wix.com/docs/rest/app-management/app-instance/app-instance-installed.md) - [App Removed](https://dev.wix.com/docs/rest/app-management/app-instance/app-instance-removed.md) ## Design guidelines The Checkout page is the final step in the customer's purchase process. Its design is closed and can't be changed by users or third parties. All Wix eCommerce sites share the same checkout experience. To ensure your plugin fits seamlessly into the checkout: - Follow the [design specs and guidelines for the Wix Checkout page](https://zeroheight.com/462ab97c4), especially the [guidelines for building plugins](https://zeroheight.com/462ab97c4/p/590051-checkout-page-slots). - Use the [Checkout UI kit and guidelines (Figma)](https://www.figma.com/file/YTGZdwAV314Gg6EdWE5gvD/Wix-Checkout---UI-Kit-and-Guidelines-(WIP-%F0%9F%9B%A0)?node-id=0%3A1&t=1FD43rEv9yTYyCa2-1) for reference styles and components. ## Testing checkout plugins To test a checkout plugin: 1. [Create a Premium development site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app/test-your-app-on-a-premium-site.md). Select Wix Stores as the business solution. 1. Install your app on the development site. 1. If your plugin requires dashboard-based installation, go to your dashboard page and add the plugin to the checkout page. 1. Initiate a checkout flow on the live site to verify your plugin appears and functions correctly. ## Related Wix backend APIs Checkout plugins usually need to integrate with Wix eCommerce's Checkout APIs, as well as other backend APIs. In your site plugin or in your app's server code, you may want to perform actions or implement logic that's dependent on the state of the current checkout or related data. The following Wix APIs may be useful: * eCommerce APIs ([JavaScript SDK](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/introduction?apiView=SDK.md), [REST](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/introduction.md), [Velo](https://dev.wix.com/docs/velo/apis/wix-ecom-backend/introduction.md)) * Stores APIs ([JavaScript SDK](https://dev.wix.com/docs/api-reference/business-solutions/stores/introduction.md), [REST](https://dev.wix.com/docs/api-reference/business-solutions/stores/introduction.md), [Velo](https://dev.wix.com/docs/velo/apis/wix-stores-v2/products/introduction.md)) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix eCommerce: Side Cart ## Article: Wix eCommerce: Side Cart ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-side-cart.md ## Article Content: # Wix eCommerce (Checkout & Orders): Side Cart The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the [Side Cart](https://support.wix.com/en/article/customizing-the-side-cart).
    Important: Some plugins may not support automatic addition upon installation, even with `autoAddToSite` enabled. In that case, you must: - [Create a dashboard page to manage your site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md) that calls [`addSitePlugin()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md) to let users add the plugin to the checkout slot. - Release at least 1 version of your app so the site plugin extension is registered with Wix for `addSitePlugin()` to work properly.
    ## Slots The following image shows slots in the Side Cart, into which users can add plugins. ![side-cart-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/21988d019e35da602895bbae036886bc.png) The slots are represented by the following `placement` object: ```json { "appDefinitionId": "", "widgetId": "", "slotId": "" } ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "49dbb2d9-d9e5-4605-a147-e926605bf164" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots).
    Supported values:
    • `"side-cart:header:after-1"`
    • `"side-cart:lineItems:after-1"`
    • `"side-cart:customer-input:after-1"`
    • `"side-cart:footer:actions:before-1"`
    • `"side-cart:footer:actions:after-1"`
    | For example, for your widget to appear before the header in the Side Cart use the following object in your configuration: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "49dbb2d9-d9e5-4605-a147-e926605bf164", "slotId": "side-cart:header:before" } ``` ## Side Cart plugin APIs Use the following APIs to integrate with the plugin's host: - [eCommerce Current Cart API](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/introduction.md) - [eCommerce frontend API](https://dev.wix.com/docs/sdk/frontend-modules/ecom/introduction.md) The Current Cart API allows you to manage and get information about the current cart. The eCommerce frontend API allows you to interact with the cart's UI elements. ### Code example ```javascript import { currentCart } from "@wix/ecom"; import { ecom } from "@wix/site-ecom"; $w.onReady(function() { // Get properties for the current cart. currentCart.getCurrentCart() .then((cart) => { const cartId = cart._id; const cartLineItems = cart.lineItems; }); // Add selected items to the cart on button click. $w('#addItems').onClick(async () => { const itemsToAdd = $w('#itemsToChoose').value; await currentCart.addToCurrentCart({lineItems: itemsToAdd }); // Call refreshCart() to refresh the cart components. await ecom.refreshCart(); }) }); ``` ## Related Wix backend APIs Side Cart plugins usually need to integrate with Wix eCommerce's Cart and Checkout APIs, as well as other backend APIs. In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the state of the current cart or related data. The following Wix APIs may be useful: * [eCommerce APIs](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/introduction.md) * [Stores APIs](https://dev.wix.com/docs/api-reference/business-solutions/stores/introduction.md) ## Design guidelines When you create a Side Cart plugin, we recommend following these design guidelines: - Make sure your design is responsive for all screen sizes. - The Side Cart uses a `4px` baseline grid for aligning all elements, typography, and spacing between elements. Don't add extra spacing around your plugin design. The slot automatically handles spacing to maintain the Side Cart's uniform layout. - Give your designs an appropriate height, depending on the slot you use. | Slot | Recommended height | Max height | | ----------------------------------- | ------------------- | ----------- | | `side-cart:header:after-1` | `30px` | `70px` | | `side-cart:line-items:after-1` | `50px` | `150px` | | `side-cart:customer-input:after-1` | `24px` | `150px` | | `side-cart:footer:actions:before-1` | `50px` | `70px` | | `side-cart:footer:actions:after-1` | `50px` | `70px` | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix eCommerce: Thank You Page ## Article: Wix eCommerce: Thank You Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-e-commerce/wix-e-commerce-thank-you-page.md ## Article Content: # Wix eCommerce (Checkout & Orders): Thank You Page The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the [Thank you page](https://dev.wix.com/docs/build-apps/develop-your-app/wix-business-solutions/wix-e-commerce/architecture-data-flow.md#thank-you-page).
    __Important:__ Some plugins may not support automatic addition upon installation, even with `autoAddToSite` enabled. In that case, you must: - [Create a dashboard page to manage your site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md) that calls [Add Site Plugin](https://dev.wix.com/docs/sdk/host-modules/dashboard/add-site-plugin.md) to let Wix users add the plugin to a Thank you page slot. - Release at least 1 version of your app so the site plugin extension is registered with Wix for `addSitePlugin()` to work properly.
    ## Slots The following image shows slots in the Thank you page, into which Wix users can add plugins. ![thank-you-page-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5de65ee544e2c63bd9bbf10f707b86e6.png) The slots are represented by the following `placement` object: ```json { "appDefinitionId": "", "widgetId": "", "slotId": "" } ``` Provide the following values for each property: | Key | Value | |-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "1380bbb4-8df0-fd38-a235-88821cf3f8a4" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots).
    Supported values:
    • `"thank-you-page:details:before"`
    • `"thank-you-page:items:after"`
    • `"thank-you-page:details:after"`
    | For example, for your widget to appear between the order summary and the shipping, billing, and payment information, use the following object in your configuration: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "1380bbb4-8df0-fd38-a235-88821cf3f8a4", "slotId": "thank-you-page:items:after" } ``` ## Thank you page plugin API The Thank you page passes props to your plugin. Use them to call [related Wix backend APIs](#related-wix-backend-apis). The plugin exposes the following props: | Name | Type | Description | |-----------|--------|-------------| | `orderId` | String | Order ID. | ### Code example The following example implements a [CLI Site Plugin](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/site-plugins/add-a-site-plugin-extension.md) and accesses the `orderId` prop. ```typescript class ThankYouPagePlugin extends HTMLElement { static get observedAttributes() { return ['order-id']; } connectedCallback() { this.render(); } attributeChangedCallback() { this.render(); } render() { const orderId = this.getAttribute('order-id') ?? ''; this.innerHTML = `
    `; } } export default ThankYouPagePlugin; ``` ## Related Wix backend APIs Thank you page plugins integrate with the Wix eCommerce Orders APIs, as well as other backend APIs. In your site plugin or in your app's server code, you can perform actions or implement logic that's dependent on the current order or related catalog data. The following Wix APIs may be useful: * [eCommerce Orders APIs](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/orders/orders/introduction.md) * [eCommerce APIs](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/introduction.md) * [Stores APIs](https://dev.wix.com/docs/api-reference/business-solutions/stores/introduction.md) ## Design guidelines When you create a Thank you page plugin, we recommend the following content and layout guidelines. ### Content guidelines - The Thank you page is part of the post-purchase confirmation flow. Keep plugin content concise and secondary to the order confirmation details. - Avoid duplicating the page's built-in order summary, totals, shipping, billing, or payment sections unless your plugin adds distinct value. ### Layout guidelines - The page layout provides `32px` spacing between sections and plugins. Don't add large outer margins around your plugin. - All slot placeholders are designed at `70px` height. - Design for compact content and responsive behavior on smaller screens. - Use the [Thank you page design spec (Figma)](https://www.figma.com/design/YWxcr1HqP7VDTz3N5h2aMT/%F0%9F%99%8F%F0%9F%8F%BC-TYP-%7C-eCom-Platform?node-id=2933-5486&t=TVMm0mVhYcNHupHh-1) for reference styles and layout. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores: Product Page ## Article: Wix Stores: Product Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-product-page.md ## Article Content: # Wix Stores: Product Page The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Product page.
    **Important:** The slots and APIs are different depending on [which version of Wix Stores](https://support.wix.com/en/article/wix-stores-customizing-your-product-page) a user has on their site. Your app should include placements for both versions. To check which version of Wix Stores is installed on a specific site, call the [Get App Instance](https://dev.wix.com/docs/rest/app-management/app-instance/get-app-instance.md) method and inspect the `installedWixApps` field.
    ## Slots The following images show the slots available in the Product page, into which users can add plugins. The locations of slots differ slightly depending on which version of Wix Stores and which page layout a site is using. ### New product page > **Note:** > If using the `product-page-media-1` slot, it may overlap with the thumbnail images on the left hand side in a desktop view. In this case, we recommend offering settings in your app to control the left padding.
    Mobile
    Desktop
    ### Old product page
    Mobile
    Classic
    Simple
    Sleek
    Spotlight
    Stunning
    The slots are represented by the following `placement` objects: ```json [ { "appDefinitionId": "", "widgetId": "", "slotId": "" }, { "appDefinitionId": "", "widgetId": "", "slotId": "" } ] ``` Provide the following values for each property: **New product page** | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "a0c68605-c2e7-4c8d-9ea1-767f9770e087" | | `widgetId` | "6a25b678-53ec-4b37-a190-65fcd1ca1a63" | | `slotId` | ID of the slot you want [as displayed in the image above](#new-product-page). | **Old product page** | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "13a94f09-2766-3c40-4a32-8edb5acdd8bc" | | `slotId` | ID of the slot you want [as displayed in the image above](#old-product-page). | For example: ```json { "appDefinitionId": "a0c68605-c2e7-4c8d-9ea1-767f9770e087", "widgetId": "6a25b678-53ec-4b37-a190-65fcd1ca1a63", "slotId": "product-page-details-2" } ``` ## Product plugin API Use the Product Plugin API to integrate with the [plugin's host](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md#terminology), which in this case is the product page. The API provides data about the current event. | Property | Type | Description | |---------------------|-----------|-------------| | `productId` | string | The ID of the product on the product page. | | `selectedVariantId` | string | The ID of the selected product variant. This ID is only available after the site visitor picks all required product choices. For example selecting the color and size of the product. Until all required choices are selected, this ID is `undefined`. | | `selectedChoices` | object | An object containing all the choices the site visitor picks from the options on the product page. Each key is an option name and each value is the selected option. For example, `{ color: "green", size: "large" }`. | | `quantity` | number | The number of product items the site visitor wants to buy. The site visitor can increase or decrease this value on the product page. | | `customText` | string[] | An array of text values entered by the site visitor in custom text fields on the product page. For example, this can be used for personalization, such as adding names to a shirt. Multiple text fields are supported, so each entry in the array corresponds to a different custom text field on the product page. | ## Related Wix APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that's dependent on the current product or related data. You can do this using the Wix Stores APIs ([Velo](https://dev.wix.com/docs/velo/apis/wix-stores-v2/products/introduction.md), [REST](https://dev.wix.com/docs/rest/business-solutions/stores/about-wix-stores.md), [JavaScript SDK](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v1/catalog/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores: Category Page ## Article: Wix Stores: Category Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-category-page.md ## Article Content: # Wix Stores: Category Page The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Category page. > **Note:** > When selecting a slot for your plugin in the Category page, you should select the same slot for the [Shop page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-shop-page.md) and the [Gallery widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-gallery-widget.md). Different Wix sites may use any of these and making these slots available for your plugin allows users to add it to their site regardless of their setup. ## Slots The following images show the slots available in the Category page, into which users can add plugins.
    Mobile
    Mobile Popup
    Desktop
    The slots are represented by the following `placement` object: ```json { "appDefinitionId": "", "widgetId": "", "slotId": "" } ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "bda15dc1-816d-4ff3-8dcb-1172d5343cce" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots). | For example: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "bda15dc1-816d-4ff3-8dcb-1172d5343cce", "slotId": "category-page-hero-top" } ``` ### Category plugin API Use the Category plugin API to integrate with the plugin's host. The API provides data about the current category. | Property | Type | Description | | -------- | ---- | ----------- | | `categoryId` | String | The ID of the category that is currently applied on the plugin's host. | ## Related Wix APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current category or related data. You can do this using the Wix Stores APIs ([Velo](https://dev.wix.com/docs/velo/apis/wix-stores-v2/products/introduction.md), [REST](https://dev.wix.com/docs/rest/business-solutions/stores/about-wix-stores.md), [JavaScript SDK](https://dev.wix.com/docs/sdk/backend-modules/stores/products/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores: Shop Page ## Article: Wix Stores: Shop Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-shop-page.md ## Article Content: # Wix Stores: Shop Page The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Shop page. > **Note:** > When selecting a slot for your plugin in the Shop page, you should select the same slot for the [Gallery widget](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-gallery-widget.md) and the [Category page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-category-page.md). Different Wix sites may use any of these and making these slots available for your plugin allows users to add it to their site regardless of their setup. ## Slots The following images show the slots available in the Shop page, into which users can add plugins.
    Mobile
    Mobile Popup
    Desktop
    The slots are represented by the following `placement` object: ```json { "appDefinitionId": "", "widgetId": "", "slotId": "" } ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "1380bba0-253e-a800-a235-88821cf3f8a4" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots). | For example: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "13afb094-84f9-739f-44fd-78d036adb028", "slotId": "gallery-products-top" } ``` ### Category plugin API Use the Category plugin API to integrate with the plugin's host. The API provides data about the current category. | Property | Type | Description | | -------- | ---- | ----------- | | `categoryId` | String | The ID of the category that is currently applied on the plugin's host. | ## Related Wix APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current category or related data. You can do this using the Wix Stores APIs ([Velo](https://dev.wix.com/docs/velo/apis/wix-stores-v2/products/introduction.md), [REST](https://dev.wix.com/docs/rest/business-solutions/stores/about-wix-stores.md), [JavaScript SDK](https://dev.wix.com/docs/sdk/backend-modules/stores/products/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Stores: Gallery Widget ## Article: Wix Stores: Gallery Widget ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-gallery-widget.md ## Article Content: # Wix Stores: Gallery Widget The following slots and APIs are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Gallery widget. > **Note:** > When selecting a slot for your plugin in the Gallery widget, you should select the same slot for the [Shop page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-shop-page.md) and the [Category page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-stores/wix-stores-category-page.md). Different Wix sites may use any of these and making these slots available for your plugin allows users to add it to their site regardless of their setup. ## Slots The following images show the slots available in the Gallery widget, into which users can add plugins.
    Mobile
    Mobile Popup
    Desktop
    The slots are represented by the following `placement` object: ```json { "appDefinitionId": "", "widgetId": "", "slotId": "" } ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "1380b703-ce81-ff05-f115-39571d94dfcd" | | `widgetId` | "13afb094-84f9-739f-44fd-78d036adb028" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots). | For example: ```json { "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", "widgetId": "13afb094-84f9-739f-44fd-78d036adb028", "slotId": "gallery-products-top" } ``` ### Category plugin API Use the Category plugin API to integrate with the plugin's host. The API provides data about the current category. | Property | Type | Description | | -------- | ---- | ----------- | | `categoryId` | String | The ID of the category that is currently applied on the plugin's host. | ## Related Wix APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current category or related data. You can do this using the Wix Stores APIs ([Velo](https://dev.wix.com/docs/velo/apis/wix-stores-v2/products/introduction.md), [REST](https://dev.wix.com/docs/rest/business-solutions/stores/about-wix-stores.md), [JavaScript SDK](https://dev.wix.com/docs/sdk/backend-modules/stores/products/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Bookings: Service Page ## Article: Wix Bookings: Service Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-bookings/wix-bookings-service-page.md ## Article Content: # Wix Bookings: Service Page Following are the slots and APIs that are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for Wix Bookings' Service page. ## Slots The Service Page can host a single plugin that users are free to reposition within the page by [reordering the Service Page sections](https://support.wix.com/en/article/wix-bookings-customizing-your-service-pages): ![service-page-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4117b92be9b9cfc8cfe4330a5e9af2ab.png) The slot is represented by the following `placement` object: ```json { "appDefinitionId": "13d21c63-b5ec-5912-8397-c3a5ddb27a97", "widgetId": "a91a0543-d4bd-4e6b-b315-9410aa27bcde", "slotId": "slot1" } ``` ## Bookings Service plugin API Use the Booking Service plugin API to integrate with the plugin's host. The API provides data about the current booking service. ### Properties | Property | Type | Description | | -------- | ---- | ----------- | | `bookingsServiceId` | String | ID of the Wix Bookings service that is currently applied on the plugin's host. | ### Code example ```javascript const {bookingsServiceId} = $widget.props; ``` ## Related Wix backend APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current booking or related data. You can do this using the Wix Bookings APIs ([Velo](https://www.wix.com/velo/reference/wix-bookings-v2/introduction) and [REST](https://dev.wix.com/docs/rest/business-solutions/bookings/about-wix-bookings.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Events: Event Details Page ## Article: Wix Events: Event Details Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-events/wix-events-event-details-page.md ## Article Content: # Wix Events: Event Details Page Following are the slots and APIs that are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Event Details Page. ## Slots The following image shows slots in the Event Details page, into which users can add plugins. ![events-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8d4c4197e9e852d402ec74c4585f4893.png) The slots are represented by the following `placement` objects: ```json { "appDefinitionId": "140603ad-af8d-84a5-2c80-a0f60cb47351", "widgetId": "14d2abc2-5350-6322-487d-8c16ff833c8a", "slotId": "header" } ``` ```json { "appDefinitionId": "140603ad-af8d-84a5-2c80-a0f60cb47351", "widgetId": "14d2abc2-5350-6322-487d-8c16ff833c8a", "slotId": "details" } ``` ## Event plugin API Use the Event plugin API to integrate with the plugin's host. The API provides data about the current event. | Property | Type | Description | | -------- | ---- | ----------- | | `eventId` | String | The ID of the event that is currently applied on the plugin's host. | ### Code example ```javascript const {eventId} = $widget.props; ``` ## Related Wix backend APIs In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current event or related data. You can do this using the Wix Events APIs ([Velo](https://www.wix.com/velo/reference/wix-events-v2/introduction) and [REST](https://dev.wix.com/docs/rest/business-solutions/events/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Wix Blog: Post Page ## Article: Wix Blog: Post Page ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/wix-blog/wix-blog-post-page.md ## Article Content: # Wix Blog: Post Page The following slots are available when building a [site plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) for the Post page. ## Slots The following image shows slots in the Post page, into which users can add plugins. ![post-slots](https://wixmp-833713b177cebf373f611808.wixmp.com/images/29722d7432e7f5c13c415900eb3cbc5c.png) The slots are represented by the following `placement` object: ```json [ { "appDefinitionId": "", "widgetId": "", "slotId": "" } ] ``` Provide the following values for each property: | Key | Value | | ------------------ | ------- | | `appDefinitionId` | "14bcded7-0066-7c35-14d7-466cb3f09103" | | `widgetId` | "211b5287-14e2-4690-bb71-525908938c81" | | `slotId` | ID of the slot you want [as displayed in the image above](#slots).
    Supported values:
    • `"above-header"`
    • `"above-content-1"`
    • `"above-content-2"`
    • `"below-content-1"`
    • `"below-content-2"`
    • `"page-bottom-1"`
    • `"page-bottom-2"`
    • `"page-bottom-3"`
    | For example: ```json { "appDefinitionId": "14bcded7-0066-7c35-14d7-466cb3f09103", "widgetId": "211b5287-14e2-4690-bb71-525908938c81", "slotId": "above-content-1" } ``` Learn more about how to [add your plugin to a slot from the dashboard page](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/build-a-dashboard-page-to-manage-your-site-plugin.md). ## Post plugin API Use the Post plugin API to integrate with the plugin's host. The API provides data about the current post. ### Properties | Name | Type | Description | | -------- | ---- | ----------- | | `postId` | String | The ID of the current post. | ### Code example ```js const { postId } = $widget.props; ``` ## Related Wix backend APIs Post plugins usually need to integrate with Wix Blog Post APIs, as well as other backend APIs. In your site plugin or in your app's server code, you may want to perform actions or implement logic that is dependent on the current post or related data. You can do this using the Wix Blog APIs ([Velo](https://dev.wix.com/docs/velo/api-reference/wix-blog-backend/introduction.md), [REST](https://dev.wix.com/docs/rest/business-solutions/blog/introduction.md), [JavaScript SDK](https://dev.wix.com/docs/sdk/backend-modules/blog/introduction.md)). --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Embedded Scripts ## Article: About Embedded Scripts ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md ## Article Content: # About Embedded Scripts An embedded script is an app extension that injects an HTML code fragment into the DOM of a site. Unlike other extensions, embedded scripts aren't fully configured by default during app installation and an extra step is required to embed the code fragment. For detailed instructions, see: * [Add a Self-Managed Embedded Script Extension](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension-to-a-self-hosted-app.md) * [Add an Embedded Script Extension Using the Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension.md) ## Example use cases Embedded scripts have a variety of uses, including: * **Analytics**: Use embedded scripts to listen to predefined Wix events as well as report and listen to custom events. Examples of predefined Wix events include clicking on a product or adding an item to a cart. For more information, see [Analyze User Behavior Using Embedded Scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/analyze-user-behavior-using-embedded-scripts.md). * **Site Interaction**: Access site data and integrate with apps made by Wix, such as Wix Stores and Wix Bookings, using the [Site API](https://dev.wix.com/docs/sdk/host-modules/site/introduction.md). The Site API is only available for embedded scripts created with the [CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md). * **Customized Behavior**: Enhance user engagement by adding dynamic features like pop-ups, sliders, or tooltips that react to user inputs. ## Embedded script types Script types determine whether site visitors consent to having your script run during their visit. Script types categorize embedded scripts for privacy and consent management purposes according to GDPR and similar data protection guidelines. For more information about meeting consent requirements and selecting a script type, see [Implement Cookie Consent Requirements](https://dev.wix.com/docs/build-apps/launch-your-app/legal-and-security/gdpr-compliance/implement-cookie-consent-requirements.md#implement-cookie-consent-requirements). Each embedded script must specify 1 of the following types: | Type | Description | Use Cases | | ------------- | ------------------------------------------------ | --------------------------------------- | | `ESSENTIAL` | Core functionality crucial to site operation. | Authentication, security features | | `FUNCTIONAL` | Remembers site visitors' choices to improve experience. | Language preferences, UI customization | | `ANALYTICS` | Provides statistics on how visitors use the site. | Google Analytics, Hotjar, Mixpanel | | `ADVERTISING` | Provides visitor data for marketing purposes. | Facebook Pixel, Google Ads, retargeting |
    Important: If a script falls into multiple types, choose the option closest to the bottom of the list (most restrictive). For example, a script with both **Analytics** and **Advertising** aspects should use `ADVERTISING`.
    ## Dynamic parameters If you have custom keys or data per user, you can use dynamic parameters in your embedded script. Dynamic parameters must: * Be strings. * Contain only alphanumeric characters (no special characters or spaces). * Be wrapped in double curly braces (`{{`). * Be enclosed in quotes (`"`) to prevent code evaluation. * Be provided upon installation when calling [Embed Script](https://dev.wix.com/docs/rest/app-management/embedded-scripts/embed-script.md). For example: ```javascript ```
    **Important:** If you add, remove, or change a dynamic parameter, users must update your app to receive the latest version of the embedded script.
    ## Troubleshooting The `MISMATCHED ANONYMOUS DEFINE() MODULES...` error is a common issue when working with AMD/UMD bundled scripts. The error occurs when an embedded script defines an anonymous module, either because it's bundled with AMD or it adds a script tag to the head containing an anonymous module. To resolve the issue, try the following: * **For the embedded script itself:** If the script is bundled with AMD/UMD, make sure that you declare only [named modules](https://requirejs.org/docs/api.html#modulename). * **For scripts loaded by the embed**: * **If you own the code:** If the scripts are bundled with AMD/UMD, make sure that you declare only [named modules](https://requirejs.org/docs/api.html#modulename). * **If you don't own the code:** Check if `requirejs` exists on the window. If it does, load the script via `requirejs` instead of adding the script to the ``. This approach is suboptimal and might still lead to race conditions and errors. For more information on this issue, see [RequireJS: Common Errors](https://requirejs.org/docs/errors.html#mismatch). ## See also * [Analyze User Behavior Using Embedded Scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/analyze-user-behavior-using-embedded-scripts.md) * [Customize Wix Stores Using Embedded Scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/customize-wix-stores-using-embedded-scripts.md) * [Add a Self-Managed Embedded Script Extension](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension-to-a-self-hosted-app.md) * [Add an Embedded Script Extension Using the Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Analyze User Behavior Using Embedded Scripts ## Article: Analyze User Behavior Using Embedded Scripts ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/analyze-user-behavior-using-embedded-scripts.md ## Article Content: # Analyze User Behavior with Embedded Scripts Use an [embedded script extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) to track analytics events on a site and send them to [external analytics platforms](https://support.wix.com/en/article/about-marketing-integrations) such as Google Analytics or Facebook Pixel. Call [trackEvent()](https://dev.wix.com/docs/sdk/host-modules/site/analytics/track-event.md) to report [standard events](https://dev.wix.com/docs/sdk/host-modules/site/events/about-analytics-events.md) or custom events to connected platforms. > **Notes:** > > - You must load the script on a site by calling [Embed Script](https://dev.wix.com/docs/api-reference/app-management/embedded-scripts/embed-script?apiView=SDK.md) in your code. > - This article shows how to track events using [an embedded script extension added by the CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension.md). You can also track events using [a self-managed embedded script extension](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension-to-a-self-hosted-app.md). ## Listen to standard Wix events Wix business solutions, such as Wix Stores or Wix Events, trigger [standard analytics events](https://dev.wix.com/docs/sdk/host-modules/site/events/about-analytics-events.md) when the corresponding visitor actions occur, but these events aren't automatically forwarded to external analytics platforms. To report them, you need to call `trackEvent()` in your embedded script code. For example, to report an [`AddToCart` event](https://dev.wix.com/docs/sdk/host-modules/site/events/wix-stores/add-to-cart.md) when a visitor adds a product to their cart: 1. In the embedded script extension's [`.html` file](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/embedded-script-extension-files-and-code.md#embeddedhtml), reference a local JavaScript module: ```html ``` 2. In the module code, import the `analytics` module and call `trackEvent()` with the standard event name and relevant data. For example, to report an `AddToCart` event: ```js import { analytics } from "@wix/site"; analytics.trackEvent("AddToCart", { id: "product-123", name: "Classic Sneakers", price: 89.99, currency: "USD", quantity: 1, }); ``` The event is reported to all analytics platforms connected to the site. ## Track custom events You can define and track custom events for visitor behaviors not covered by standard events, and report them to connected external analytics platforms: 1. In the [embedded script extension's `.html` file](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/embedded-script-extension-files-and-code.md#embeddedhtml), reference a local JavaScript module: ```html ``` 2. In the module code, import the `analytics` module and call `trackEvent()` with the event name and any relevant event data: ```js import { analytics } from "@wix/site"; analytics.trackEvent("HeroBannerClick", { eventCategory: "engagement", eventAction: "hero-banner-click", }); ``` The event is reported to all analytics platforms connected to the site. > **Note:** Some external analytics platforms require that the custom event contains certain properties, such as `eventCategory` and `eventAction`. ## Register event handlers You can call [registerEventListener()](https://dev.wix.com/docs/sdk/host-modules/site/analytics/register-event-listener.md) to register an event handler that runs whenever any analytics event occurs, either standard or custom. Use this for debugging, logging, or forwarding events to a custom destination. To run handlers for only specific event types, use conditional logic in your code: ```js import { analytics } from "@wix/site"; analytics.registerEventListener((eventName, eventData) => { switch (eventName) { case "PageView": console.log("Page viewed:", eventData); break; case "AddToCart": console.log("Added to cart:", eventData); break; case "HeroBannerClick": console.log("Hero banner clicked:", eventData); break; } }); ``` ## See also - [About Marketing Integrations](https://support.wix.com/en/article/about-marketing-integrations) - [Add an Embedded Script Extension Using the CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension.md) - [Add a Self-Managed Embedded Script Extension](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension-to-a-self-hosted-app.md) - [About the Analytics API](https://dev.wix.com/docs/sdk/host-modules/site/analytics/introduction.md) - [About Analytics Events](https://dev.wix.com/docs/sdk/host-modules/site/events/about-analytics-events.md) - [About Embedded Scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Customize Wix Stores Using Embedded Scripts ## Article: Customize Wix Stores Using Embedded Scripts ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/customize-wix-stores-using-embedded-scripts.md ## Article Content: # Customize Wix Stores Using Embedded Scripts
    **Warning:** This feature isn't supported in Wix Studio sites. Instead, use a [site plugin extension](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/about-site-plugin-extensions.md) with a Wix Stores [slot](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-plugins/supported-wix-app-pages/about-slots.md).
    [Wix Stores](https://support.wix.com/en/article/wix-stores-about-wix-stores) provides placeholders in their default product page where you can extend the functionality by injecting embedded scripts: ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ac/d0d6e5d7-1e68-49aa-8fbd-c4454894592e/2020/01/02/af158566-0a4d-4a51-845b-86ca332695fa/b32408db-96ac-4522-9df4-294e901931d7.png) ## Step 1 | Locate the placeholders Below the product SKU you should find: ```html
    ``` And at the bottom of the product page you should find: ```html
    ``` The placeholders are empty divs with data-hooks that allow you to add content to these specific page locations. ## Step 2 | Inject data into a placeholder To inject data into a placeholder, follow these steps: 1. Develop the HTML content or elements you want to insert into the placeholder. 1. Write a JavaScript script to append your content to the relevant placeholder. Ensure your script waits until the page is fully loaded by using the `DOMContentLoaded` event or a similar method. The following example appends a new paragraph to the `details-placeholder`: ```javascript document.addEventListener('DOMContentLoaded', function() { var placeholder = document.querySelector('[data-hook="details-placeholder"]'); if (placeholder) { var newElement = document.createElement('p'); newElement.textContent = 'This is the new content.'; placeholder.appendChild(newElement); } }); ``` 1. Add the script to your app using an embedded script extension. You can do this in your app's dashboard or via the CLI. For detailed instructions, refer to the following resources: * [Add a Self-Managed Embedded Script Extension](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension-to-a-self-hosted-app.md) * [Add an Embedded Script Extension Using the Wix CLI](https://dev.wix.com/docs/wix-cli/guides/extensions/site-extensions/embedded-scripts/add-an-embedded-script-extension.md)
    **Important:** Make sure you append your DOM elements to the placeholder's existing content. Don't replace the content as other apps may be using the same placeholder.
    ## See also * [About Embedded Scripts](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/embedded-scripts/about-embedded-scripts.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Editor React Components ## Article: About Editor React Components ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md ## Article Content: # About Editor React Component Extensions
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Editor React Component extensions let you build custom site widgets in React that fully integrate with the editor. You describe your component's customizable parts in a declarative manifest, and the editor builds matching [auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) for Wix users. This gives Wix users the same editing experience they get with built-in Wix elements, without you having to build or maintain your own settings panels like you would with a custom element. To see auto panels appear one by one as you add manifest properties, follow the [step-by-step tutorial](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/tutorial-configure-auto-panels.md).
    **Editor Compatibility** Editor React Component extensions are built for [Wix Harmony](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md), Wix's AI-powered editor. They're not supported on Wix Editor or Wix Studio sites, and there's no way to conditionally switch between extension types based on the editor.
    For a walkthrough of Editor React Components, watch the video tutorial:
    ## How it works Your extension has 2 main parts: - **React component**: Your UI code that renders in the editor and on the live site. - **Manifest**: A configuration object that declares what's customizable about your component, so the editor knows which [auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) and controls to show to Wix users. You build the React component first. The CLI then auto-generates the large part of the manifest for you. For more information, see [Auto-generated manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md). Your component code may also include additional assets like stylesheets, and you can provide separate bundles for the live site and the editor. For more details, see [`resources`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/resources.md). When a Wix user adds your component to their site: 1. The editor reads the manifest to understand what customizations are available. 2. The editor builds the matching auto panels and controls in the editor UI. 3. As the Wix user makes changes, the editor stores those values. It passes data changes to your component as [props](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md) and applies style changes as CSS directly to the DOM elements that match the selectors in the manifest. TypeScript types for all props are available from the [`@wix/editor-react-types`](https://www.npmjs.com/package/@wix/editor-react-types) package. ```mermaid %%{ init: { 'theme': 'base', 'themeVariables': { 'primaryColor': '#ffffff', 'primaryTextColor': '#162D3D', 'primaryBorderColor': '#162D3D', 'lineColor': '#0C6EFC', 'secondaryColor': '#674FE6', 'tertiaryColor': '#F4F4F4', 'edgeLabelBackground': '#ffffff', 'labelTextColor': '#162D3D' } } }%% graph TD subgraph Development ["Your code"] direction LR A1[React component] --- A2[Manifest] end Development --> B([Wix user installs your app]) B --> WixEditor subgraph WixEditor ["Editor"] C[Editor reads manifest to build auto panels]--> D{User interaction} D -->|Data| E[Delivered as props] D -->|Style| F[Applied via CSS selectors] E --> G[Component rerenders] F --> G end %% Custom Wix-y Styling style Development fill:#F0EBFF,stroke:#674FE6,stroke-width:2px style WixEditor fill:#D5E6FF,stroke:#0C6EFC,stroke-width:2px style D fill:#FFF9E5,stroke:#FAAD4D style B fill:#E6F0FF,stroke:#0C6EFC ``` ## Example Here's a testimonial card built as an Editor React Component. The component code comes first, then the manifest that drives it. **React component:** ```tsx import type { Image } from '@wix/editor-react-types'; import './style.css'; interface TestimonialCardProps { className: string; quote?: string; authorName?: string; authorRole?: string; image?: Image; } export default function TestimonialCard({ className, quote, authorName, authorRole, image }: TestimonialCardProps) { return (
    {quote ?? '"This app transformed our workflow."'}
    {image?.url && {authorName}

    {authorName ?? 'Jordan Lee'}

    {authorRole ?? 'Operations Manager'}

    ); } ```
    Sample stylesheet (style.css) ```css .testimonial-card { display: flex; flex-direction: column; gap: 20px; padding: 28px; border-radius: 12px; font-family: sans-serif; } .quote { margin: 0; font-size: 16px; line-height: 1.6; font-style: italic; } .author { display: flex; align-items: center; gap: 12px; } .avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; } .author-name { margin: 0; font-weight: 600; } .author-role { margin: 0; font-size: 13px; color: #6b7280; } ```
    Notice how the component applies `className` to the root element and uses CSS classes like `.quote` and `.author-name` on inner elements. These patterns are part of the [component requirements](#component-requirements) that let the editor manage your component. **Manifest:** ```json { "type": "yourAppCodeIdentifier.testimonialCard", "description": "A customizable testimonial card", "resources": { ... }, "editorElement": { "selector": ".testimonial-card", "displayName": "Testimonial Card", "data": { "quote": { "dataType": "text", "displayName": "Quote" }, "authorName": { "dataType": "text", "displayName": "Author Name" }, "authorRole": { "dataType": "text", "displayName": "Author Role" }, "image": { "dataType": "image", "displayName": "Author Photo" } }, "cssProperties": { "backgroundColor": { "defaultValue": "#ffffff" }, "padding": { "defaultValue": "28px" }, "borderRadius": { "defaultValue": "12px" }, "boxShadow": { "defaultValue": "0 1px 3px rgba(0,0,0,0.08)" } } } } ``` The `selector` (`.testimonial-card`) matches the CSS class on the component's root element. Everything else in the manifest drives an auto panel. Each `data` property with `dataType: "text"` becomes an editable text field. When a Wix user clicks **Edit Text** on the component, the editor builds an Edit Text auto panel with inputs for every text property. The `displayName` value appears as the label for each input: ![The Edit Text panel, with annotations showing how each text data property in the manifest maps to a field in the panel](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ff1a30b6a7d7af5981ff5006f6de9cf4.png) Properties with `dataType: "image"` surface in the **Settings** auto panel, where Wix users can upload or choose images from the Wix Media Manager: ![The Settings panel, with annotations showing how the image data property in the manifest maps to the image upload control](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0de9a85121d8df161fa570b4dbba7423.png) The `cssProperties` configure a full **Design** auto panel with controls for fill colors, borders, corners, shadows, and spacing. The editor maps each CSS property to the appropriate design control: ![The Design panel, with annotations showing how cssProperties in the manifest map to the design controls](https://wixmp-833713b177cebf373f611808.wixmp.com/images/868c0d3de03e7b08fecb20755c9775b3.png) > **Note:** These are a subset of the auto panels available. The full action bar is built from your manifest, and you never write panel UI code. For the full list, see [About Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md). ## Component requirements For the editor to style, select, and configure your component, it needs to follow these conventions: - Export a default function component. - Accept a `className` prop and apply it to the root element. The editor uses this to manage styles and layout. - Add selectors to elements you want the manifest to target. The manifest accepts any valid DOM selector. CSS classes are the recommended approach. - Name your data props to match the keys in the manifest's `data` definition. Some manifest features may require additional component-side setup, such as a wrapper element for presets and prop handling for interaction states. The [tutorial on configuring auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/tutorial-configure-auto-panels.md) covers these as they come up. ## Runtime props As Wix users edit your component, the editor passes updated values to it as React props. Data values like text and numbers arrive as top-level props, while style changes are applied directly to the DOM as inline CSS. Some data types, like images and vector art, use complex object formats at runtime. The [`@wix/editor-react-types`](https://www.npmjs.com/package/@wix/editor-react-types) package provides TypeScript types for these values. For the full breakdown of prop categories, see [Runtime Props](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md). ## What you can configure The testimonial card example covers a simple case: editable text, an image, and some design properties. Most components go further. A slideshow has navigation arrows, a menu has items with hover states, a card grid needs layout controls. Through the manifest, you can also configure inner elements with their own auto panels, interaction states, layout and resize behavior, presets, animations, and richer data types. For the full set of auto panels and the manifest properties that drive each one, see [About Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md). For the per-property manifest reference, see [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md). ## Get started You can [add an Editor React Component extension using the CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md). The CLI scaffolds your component files, auto-generates the `editorElement` portion of the manifest from your React code, handles bundling, and manages deployment. From there, you can develop your component: - **With an AI agent**: Use [Wix skills](https://dev.wix.com/docs/api-reference/articles/ai-tools/about-wix-skills.md) from a supported AI coding agent to build and update components. The skills regenerate the manifest as it works. - **Manually**: Edit the component source directly. After manual changes, run [`wix generate manifest`](https://dev.wix.com/docs/wix-cli/command-reference/project-commands/app-only/generate-manifest.md) to update the auto-generated manifest. ## Limitations The following limitations apply to Editor React Components. For manifest-specific rules and unsupported features, see [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md). - **Custom panels aren't supported.** Only [auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) are available. You can't ship your own React UI for an editor panel, so anything Wix users can configure has to map to a manifest property. - **Self-hosting**: Self-hosting for Editor React Component extensions isn't supported. Deploy components using the [CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md). ## See also - [About Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [Tutorial | Configure Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/tutorial-configure-auto-panels.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Auto Panels ## Article: About Auto Panels for Editor React Components ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md ## Article Content: # About Auto Panels for Editor React Components
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Auto panels are how Wix users customize your [Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) in the editor. When a Wix user selects your component, Wix builds the panels automatically from the declarations in its manifest. You don't render any UI yourself. The CLI auto-generates most of those declarations in the [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) portion of the manifest by reading your React component source. For details, see [auto-generated manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md#auto-generated-manifest). To see auto panels built up from scratch, see [Tutorial | Configure Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/panels/tutorial-configure-auto-panels.md). ## How Wix users open auto panels When a Wix user selects your component in the editor, an action bar appears with buttons for the auto panels available on it. Each button opens a panel populated with the controls you declared. Auto panels are available for the root component and, if you define them, for [inner elements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md). Inner elements can be selected independently, each with its own action bar and auto panels. > **Note:** Auto panels appear only in the editor, while a Wix user is editing the site. They aren't shown on the live site. ## Available auto panels and controls The following panels and controls appear in the editor for your component, depending on which manifest properties you define. - [Settings](#settings) - [Edit Text](#edit-text) - [Design](#design) - [Explore Designs](#explore-designs) - [Animation](#animation) - [Action bar buttons](#action-bar-buttons) - [Sizing behavior](#sizing-behavior) ### Settings The **Settings** panel exposes editable content for your component. Each entry in your manifest's [`data`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) becomes a labeled control, with the `dataType` determining the control type. For example, text becomes a text input, images open the Wix Media Manager, and dates show a date picker. The values Wix users enter here are passed back to your component as React [props](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md) at runtime. ![The Settings panel for a testimonial card, with controls for an image data item](https://wixmp-833713b177cebf373f611808.wixmp.com/images/0de9a85121d8df161fa570b4dbba7423.png) ### Edit Text When your `data` includes one or more text-based items (`text`, `richText`, `textEnum`), an **Edit Text** panel is generated alongside **Settings**, exposing only the text inputs. This gives Wix users a focused way to update copy without scrolling through unrelated controls. ![The Edit Text panel with inputs for quote, author name, and author role](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ff1a30b6a7d7af5981ff5006f6de9cf4.png) ### Design The **Design** panel exposes style controls. Each entry in [`cssProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md) and [`cssCustomProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md) you declare becomes the appropriate control: `backgroundColor` gets a color picker, `borderRadius` gets a corner radius slider, `boxShadow` gets a shadow editor, and so on. When a Wix user changes a value, the editor applies it as an inline style on the matching DOM element. Your component doesn't need to read or handle these values. ![The Design panel with color, spacing, corners, and shadow controls](https://wixmp-833713b177cebf373f611808.wixmp.com/images/868c0d3de03e7b08fecb20755c9775b3.png) If your manifest also defines [`states`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md), a state selector appears at the top of the **Design** panel so Wix users can style each state separately. ### Explore Designs If your manifest defines [`presets`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md), an **Explore Designs** panel appears. Each preset is a named visual variation with its own thumbnail, default values, and optional preset-scoped CSS. Switching between presets is a one-click change for Wix users. ![The Explore Designs panel with Card and Minimal preset variants](https://wixmp-833713b177cebf373f611808.wixmp.com/images/794cd89fcb40d5e52f00110c4195f1b4.png) ### Animation If your manifest defines [`interactions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/interactions.md), an **Animation** panel appears with controls for declarative entrance effects, scroll triggers, and other interactions you opted into. ### Action bar buttons The action bar that opens your auto panels also hosts non-panel buttons, like duplicating the component or opening a Wix-managed picker. You can opt out of the defaults with [`actions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md), and you can add your own buttons with [`customActions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md) that open URLs or trigger custom behavior. ### Sizing behavior The editor reads [`layout`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout.md) to enable resize handles, content sizing, and stretch behavior in the editor, and [`installation.initialSize`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/installation.md) to set the dimensions when the Wix user first drops the component on the page. ## Control what's in a panel You configure panel contents through manifest properties. The properties you define determine which controls appear and how they behave. - **Include or omit controls:** Only properties you declare in the manifest appear in a panel. For example, skip a `cssProperty` and that control doesn't show up. - **Set labels:** Use [`displayName`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) on data items, CSS properties, presets, and elements to control the text Wix users see. - **Set default values:** Use `defaultValue` so controls show the same value the Wix user sees in the editor. - **Constrain input:** Each data type accepts type-specific options that the editor enforces in the control. For example, `text.minLength`, `text.maxLength`, and `number.minimum`. ## Organize controls within a panel Use grouping and conditional visibility to keep each panel focused and easier to scan. - **Group related controls:** Use [`displayGroups`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-groups/display-groups.md) to organize properties into named sections inside a panel. - **Show controls conditionally:** Use [`displayFilters`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md) to hide controls based on the value of another data item, preset, or state. ## Customize inner elements and states You can expose advanced customization beyond the root component by configuring inner targets and state-based styling. - **Configure inner elements:** Each entry in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) gets its own set of auto panels. Add `data`, `cssProperties`, `presets`, or `states` inside an element to customize its panels independently of the root. - **Expose state-specific styling:** Define [`states`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) (like hover or disabled) for per-state styling. ## Manifest cheatsheet Use this as a quick lookup for which manifest property configures which part of the editor experience: | Manifest property | Where it surfaces | What it controls | |---|---|---| | [`data`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) | **Settings** panel, **Edit Text** panel, and other native controls based on the `dataType` | Editable content like text, numbers, images, links, dates, and toggles. Each entry becomes a control and a runtime prop. | | [`cssProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md) | **Design** panel | Standard CSS properties exposed as design controls (colors, spacing, corners, shadows, typography). Applied as inline styles. | | [`cssCustomProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md) | **Design** panel | CSS variables for cases the standard properties don't cover. Applied as inline custom property values. | | [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) | Independent action bar and auto panels on inner parts | Makes specific inner DOM elements selectable, each with its own data, CSS, presets, and states. | | [`presets`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md) | **Explore Designs** panel | Named visual variations with thumbnails, default values, and optional preset-scoped CSS. | | [`states`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) | **State** selector in the **Design** panel | Conditions like hover or disabled, each stylable independently in the **Design** panel. | | [`interactions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/interactions.md) | **Animation** panel | Declarative animation triggers and effect groups. | | [`actions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md) | Action bar | Opt out of, override, or rename default action bar buttons. | | [`customActions`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md) | Action bar | Add your own buttons to the action bar that open URLs or trigger custom behavior. | | [`displayGroups`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-groups/display-groups.md) | Inside auto panels | Organize related controls into named sections. | | [`displayFilters`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md) | Inside auto panels | Show or hide controls based on the value of another data item, preset, or state. | | [`layout`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout.md) | Editor (resize handles and stretching) | Resize direction, content sizing, and stretch behavior. | | [`installation.initialSize`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/installation.md) | Editor (on insert) | The size the component takes when a Wix user first adds it to the page. | ## See also - [Tutorial | Configure Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/panels/tutorial-configure-auto-panels.md) - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [Editor Element](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) - [Runtime Props Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Configure Auto Panels ## Article: Configure Auto Panels ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/tutorial-configure-auto-panels.md ## Article Content: # Tutorial | Configure Auto Panels for an Editor React Component
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    When a Wix user adds your [Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) to a page, they customize it through [auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) that the editor builds automatically. You don't write any panel UI. You declare what's customizable in a [manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md), and the matching native controls show up. This tutorial walks through that declaration, step by step. You'll start with a minimal manifest that just renders the component in the editor with no controls, and then add one manifest field at a time. After each step, you'll see a new auto panel (or a new control inside one) appear in the editor. Here's what we'll cover: 1. Start with a minimal manifest that renders the component without panels. 2. Add editable content for the **Settings** and **Edit Text** panels. 3. Expose CSS properties for the **Design** panel. 4. Define inner elements, each with its own action bar and auto panels. 5. Configure layout and resize behavior in the editor. 6. Set the initial size when the component is first added. 7. Create visual presets for the **Explore Designs** panel. 8. Add interaction states like hover for a state selector in the **Design** panel. Each step builds on the previous. At the end, you'll have a fully configured manifest and a clear sense of which auto panel each manifest field drives.
    **Editor Compatibility** Editor React Component extensions are built for [Wix Harmony](https://dev.wix.com/docs/build-apps/get-started/overview/about-wix-harmony-and-apps.md), Wix's AI-powered editor. They're not supported on Wix Editor or Wix Studio sites, and there's no way to conditionally switch between extension types based on the editor.
    ## Before you begin Before you begin, make sure that you: - [Create an Editor React Component extension with the CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md). - Install the [`@wix/editor-react-types`](https://www.npmjs.com/package/@wix/editor-react-types) package for TypeScript types used by the editor. ## The component This guide uses a testimonial card that includes a quote, author info, and photo. Here's the React component: ```tsx import type { Image } from '@wix/editor-react-types'; import './style.css'; interface TestimonialCardProps { className: string; quote?: string; authorName?: string; authorRole?: string; image?: Image; } export default function TestimonialCard({ className, quote, authorName, authorRole, image }: TestimonialCardProps) { return (
    {quote ?? '"This app transformed our workflow."'}
    {image?.url && {authorName}

    {authorName ?? 'Jordan Lee'}

    {authorRole ?? 'Operations Manager'}

    ); } ```
    Stylesheet (style.css) ```css .testimonial-card { display: flex; flex-direction: column; gap: 20px; padding: 28px; border-radius: 12px; font-family: sans-serif; } .quote { margin: 0; font-size: 16px; line-height: 1.6; font-style: italic; } .author { display: flex; align-items: center; gap: 12px; } .avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; } .author-name { margin: 0; font-weight: 600; } .author-role { margin: 0; font-size: 13px; color: #6b7280; } ```
    For the editor to work with your component, it needs to follow the [component requirements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md#component-requirements). Later steps introduce additional component-side requirements for [presets](#step-7--add-presets) and [states](#step-8--add-states). ## Step 1 | Minimal manifest Every manifest needs 3 things: - `type` to identify the component. - `resources` to tell the editor where to find the code. - `editorElement` with at least a `selector` and `displayName`. Here's a minimal manifest for our example component: ```typescript { type: 'myAppCodeIdentifier.testimonialCard', resources: { client: { componentUrl: './extensions/site/components/testimonial-card/component.js', }, }, editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', }, } ``` Here's what each field does: - `type`: A unique identifier for your component. - `resources.client.componentUrl`: Points to your React component file, relative to the project root. When you [create your Editor React Component using the CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md) command, this field is configured automatically. - `selector`: Must match a CSS class on your component's root DOM element. For this component, that's `.testimonial-card`. The editor uses this selector to locate your component and apply styling. - `displayName`: The label Wix users see when they select your component. ### What changes in the editor At this point, the component renders in the editor but has no editing controls. A Wix user can place it on a page, but they can't change any content or styling. ![Base component rendered in the editor with no editing controls](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c3c13458816cce0d249dcfdc570e5cec.png) ## Step 2 | Add data The `data` property defines what content Wix users can edit. Each key in the `data` object becomes a prop that your component receives at runtime. The `dataType` determines what kind of editor control appears: a text field, image picker, toggle, and so on. ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { quote: { dataType: 'text', displayName: 'Quote', defaultValue: '"This app transformed our workflow."', text: { maxLength: 500, }, }, authorName: { dataType: 'text', displayName: 'Author Name', defaultValue: 'Jordan Lee', }, authorRole: { dataType: 'text', displayName: 'Author Role', defaultValue: 'Operations Manager', }, image: { dataType: 'image', displayName: 'Author Photo', }, }, }, ``` Each data item has a few core fields: - `dataType`: Determines the editor control and the runtime value format. `'text'` gives a text input, `'image'` gives a media picker. - `displayName`: The label Wix users see in the editor panel. - `defaultValue`: The starting value shown in the editor panel. This value isn't passed to your component at runtime, so your component should handle its own fallback rendering. Some data types support additional configuration in a field named after the type. Here, `quote` uses the `text` field to set `maxLength: 500`, which limits the text input to 500 characters. The data keys `quote`, `authorName`, `authorRole`, and `image` must match the prop names in your component. The editor passes these as top-level props when it renders your component. For the full list of data types, see [Data Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-overview.md). ### What changes in the editor A **Settings** button appears in the action bar. Clicking it opens a panel with text inputs for the 3 text properties. A **Media** button also appears, opening the Wix Media Manager for the image property. ![Settings panel with editable text inputs and media picker](https://wixmp-833713b177cebf373f611808.wixmp.com/images/300d9ea5600fc8b8b928afb76fceb234.png) ## Step 3 | Add CSS properties The `cssProperties` property defines which CSS styles Wix users can customize. The editor reads these definitions and configures the appropriate design panel controls. When a Wix user changes a value, the editor applies it as an inline style on the DOM element matching the `selector`. Your component doesn't need to read or handle these values. > **Note:** Not all CSS properties are supported. For more information, see [Standard CSS Properties](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md). ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { ... }, cssProperties: { backgroundColor: { defaultValue: '#ffffff', }, padding: { defaultValue: '28px', }, borderRadius: { defaultValue: '12px', }, boxShadow: { defaultValue: '0 1px 3px rgba(0,0,0,0.08)', }, }, }, ``` Each CSS property item can include: - `defaultValue`: The initial CSS value shown in the design panel. This should match the value already set in your component's CSS file. It tells the editor what the current default is so users can see it before making any changes. - `displayName`: Optional label. If omitted, the editor derives a name from the property name. If you don't need a custom default, you can pass an empty object (for example, `backgroundColor: {}`). The editor still shows the appropriate control and uses its own default value. The editor maps each CSS property to the appropriate control automatically: `backgroundColor` gets a color picker, `borderRadius` gets a corner radius control, `boxShadow` gets a shadow editor, and `padding` gets spacing inputs. ### What changes in the editor A **Design** button appears in the action bar. Clicking it opens a panel with color, spacing, corners, and shadow controls, all configured from the 4 CSS properties defined above. ![Design panel with color, spacing, corners, and shadow controls](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f4c91991f19fa63f8b049650d2f548c2.png) > **Note:** Your CSS file styles the component on initial render. The manifest's `defaultValue` tells the design panel what to display as the starting value, and it should match what's already in your CSS. When a Wix user customizes a value, the editor applies it as an inline style, which overrides both. For the full reference, see [CSS Properties Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md). ## Step 4 | Add elements So far, all customization targets the root element. But the testimonial card has distinct parts that Wix users might want to style independently, such as the quote text and the author section. The `elements` property lets you make inner parts of your component selectable and configurable. Each element needs its own `selector` (matching a CSS class in your component), a `displayName`, and optionally its own `data`, `cssProperties`, and `behaviors`. For the full reference on what elements support, see [Elements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md). ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { ... }, cssProperties: { ... }, elements: { quoteText: { elementType: 'inlineElement', inlineElement: { selector: '.quote', displayName: 'Quote Text', behaviors: { selectable: true, }, cssProperties: { color: { defaultValue: '#1f2937', }, fontSize: { defaultValue: '16px', }, fontStyle: { defaultValue: 'italic', }, }, }, }, authorNameText: { elementType: 'inlineElement', inlineElement: { selector: '.author-name', displayName: 'Author Name', behaviors: { selectable: false, }, cssProperties: { color: { defaultValue: '#111827', }, fontWeight: { defaultValue: '600', }, fontSize: { defaultValue: '14px', }, }, }, }, authorRoleText: { elementType: 'inlineElement', inlineElement: { selector: '.author-role', displayName: 'Author Role', behaviors: { selectable: false, }, cssProperties: { color: { defaultValue: '#6b7280', }, fontSize: { defaultValue: '13px', }, }, }, }, }, }, ``` Each element is wrapped in an `elementType` and configuration object. The only supported type is `'inlineElement'`, an element inside your component code. The `behaviors` field controls how elements respond to interactions in the editor: - `selectable`: Whether a Wix user can click on this part independently. Elements are not selectable by default, so you must set this to `true` for any element you want users to select directly. - `removable`: Whether Wix users can remove or restore this element in the editor. When removed, the element is hidden on the live site. > **Note:** Elements can also define their own `data` properties. When they do, the values aren't passed as top-level props. Instead, they arrive in an `elementProps` object, nested by element key. This example only uses `cssProperties` on elements, so `elementProps` doesn't apply here. For details, see [Runtime Props](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md). Here, `quoteText` has `selectable: true`, so a Wix user can click the quote to see and edit its specific design controls. The author name and role are set to `selectable: false`, so their styling is accessible through the parent's design panel but they can't be clicked independently. This avoids cluttering the editing experience with too many selectable targets. For the full reference, see [Elements Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md). ### What changes in the editor The quote text becomes independently selectable. When a Wix user clicks on it, the design panel shows controls specific to the quote (color, font size, and font style), separate from the card's overall design controls. ![Quote text selected with its own design controls](https://wixmp-833713b177cebf373f611808.wixmp.com/images/5dab3dac4037c2e41522047c69ca2654.png) ## Step 5 | Add layout The `layout` property controls how a Wix user can resize and position the component in the editor. ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { ... }, cssProperties: { ... }, elements: { ... }, layout: { resizeDirection: 'horizontal', contentResizeDirection: 'vertical', disableStretching: false, }, }, ``` 2 fields drive most layout behavior: - `resizeDirection`: Which directions the Wix user can drag to resize. `'horizontal'` means they can adjust the width but not the height. - `contentResizeDirection`: Which directions the component automatically resizes to fit its content. `'vertical'` means the height adjusts as content grows or shrinks. Together, these create a common pattern: the Wix user controls the width, and the height auto-adjusts to fit the content. This works well for text-heavy components like the testimonial card. Setting `disableStretching: false` lets the Wix user stretch the component to fill the width of its container. For the full reference and common patterns, see [Layout Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout/layout.md). ### What changes in the editor Resize handles appear on the left and right edges. Dragging them adjusts the width, and the height automatically follows the content. ![Resize handles on the component for horizontal resizing](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f53c43c44606c83d3bfdc108fd1bdb0d.gif) ## Step 6 | Add installation The `installation` property controls how the component appears when a Wix user first adds it to a page. The most important field is `initialSize`, which sets the starting dimensions. ```typescript { type: 'myAppCodeIdentifier.testimonialCard', resources: { client: { componentUrl: './extensions/site/components/testimonial-card/component.js', }, }, installation: { initialSize: { width: { sizingType: 'pixels', pixels: 400, }, height: { sizingType: 'content', }, }, }, editorElement: { ... }, } ``` > **Note:** `installation` is a property on the same level as `editorElement`, not inside `editorElement`. Each dimension uses a sizing type: - `'pixels'`: A fixed pixel value. The component starts at exactly this size. - `'content'`: The dimension fits to the component's content. - `'stretched'`: The dimension fills its parent container. Here, the card starts 400 pixels wide with the height driven by content. This works with the `layout` from the previous step: the width is a starting point that the Wix user can then resize horizontally, and the height always follows the content. For the full reference, see [Installation Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/installation.md). ### What changes in the editor Installation doesn't produce a visible change in the editor UI. The effect is only apparent when a Wix user first adds the component to a page and it appears at the configured size. ## Step 7 | Add presets Presets let you offer distinct visual variants of your component. Each preset can include a CSS file, layout overrides, and default values for CSS properties and elements. Use presets for significant differences between variants, like layout changes or major style shifts. For smaller customizations, CSS properties give users direct control without switching the entire preset. For presets to work, your component needs a wrapper element with `presetsWrapperProps`. Add `Wix` from `@wix/editor-react-types` to your props interface and destructure it from props: ```tsx import type { Image, Wix } from '@wix/editor-react-types'; interface TestimonialCardProps { className: string; quote?: string; authorName?: string; authorRole?: string; image?: Image; wix?: Wix; } export default function TestimonialCard({ className, quote, authorName, authorRole, image, wix }: TestimonialCardProps) { return (
    {/* component content */}
    ); } ``` > **Note:** When using `presetsWrapperProps`, place `className` on the inner element, not the wrapper. The wrapper renders with `display: contents` and is invisible to the editor. The inner element is what the editor tracks for selection and layout. Then define presets in the manifest: ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { ... }, cssProperties: { ... }, elements: { ... }, layout: { ... }, presets: { card: { displayName: 'Card', thumbnailUrl: 'https://example.com/thumbnails/card-thumbnail.png', presetCssUrl: './extensions/site/components/testimonial-card/presets/card.css', presetDefaults: { cssProperties: { backgroundColor: { defaultValue: '#ffffff' }, borderRadius: { defaultValue: '12px' }, boxShadow: { defaultValue: '0 1px 3px rgba(0,0,0,0.08)' }, }, }, }, minimal: { displayName: 'Minimal', thumbnailUrl: 'https://example.com/thumbnails/minimal-thumbnail.png', presetCssUrl: './extensions/site/components/testimonial-card/presets/minimal.css', presetDefaults: { cssProperties: { backgroundColor: { defaultValue: 'transparent' }, borderRadius: { defaultValue: '0px' }, boxShadow: { defaultValue: 'none' }, }, }, }, }, }, ``` Each preset supports 2 optional fields: - `presetCssUrl`: Path to a CSS file scoped to this preset, relative to the project root. Use this for style rules that go beyond what `presetDefaults` can express, such as layout changes, font declarations, or pseudo-element styling. - `thumbnailUrl`: A valid web URL (`http://` or `https://`) to a preview image shown in the **Explore Designs** panel. Each preset's `presetDefaults` replaces the base configuration when a Wix user switches to that preset. The `'card'` preset shows the card with a white background and shadow. The `'minimal'` preset strips those away for a clean, borderless look. When using presets, you also need to map screen sizes to preset keys in `installation`. The `large` key applies to desktop and tablet, and `small` applies to mobile: ```typescript installation: { presets: { large: 'card', small: 'card', }, initialSize: { ... }, }, ``` For the full reference, see [Presets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md). ### What changes in the editor An **Explore Designs** panel appears where Wix users can switch between "Card" and "Minimal" preset variants. ![Explore Designs panel with Card and Minimal preset variants](https://wixmp-833713b177cebf373f611808.wixmp.com/images/794cd89fcb40d5e52f00110c4195f1b4.png) ## Step 8 | Add states States let Wix users style your component differently based on conditions like hover. You define a state in the manifest using up to 3 mechanisms: - A CSS pseudo-class (like `:hover`) for the live site. - A CSS class for your stylesheet. - Props that your component reads to preview the state in the editor. ```typescript editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { ... }, cssProperties: { backgroundColor: { defaultValue: '#ffffff', statesDefaultValues: { hover: '#f9fafb', }, }, boxShadow: { defaultValue: '0 1px 3px rgba(0,0,0,0.08)', statesDefaultValues: { hover: '0 4px 12px rgba(0,0,0,0.12)', }, }, padding: { ... }, borderRadius: { ... }, }, elements: { ... }, layout: { ... }, states: { hover: { displayName: 'Hover', pseudoClass: 'hover', className: 'is-hovered', props: { hovered: true, }, }, }, }, ``` Notice that `backgroundColor` and `boxShadow` now include `statesDefaultValues`. These set the initial design panel defaults for each state, so the editor knows what values to show when the Wix user switches to the hover state. The `hover` state uses all 3 mechanisms: - `pseudoClass: 'hover'`: Maps to CSS `:hover` on the live site. The browser handles this automatically. Note that `:hover` has no effect on touch/mobile devices. - `className: 'is-hovered'`: Applies this CSS class when the state is active, so you can target it in your stylesheet. - `props: { hovered: true }`: Passes this prop to your component when the state is selected in the editor, so you can show the hover appearance on stage. Your component code uses the `hovered` prop to apply the class on the editor stage: ```tsx export default function TestimonialCard({ className, hovered, ...props }: TestimonialCardProps & { hovered?: boolean }) { return (
    {/* ... */}
    ); } ``` Your CSS needs to target both the class and the pseudo-class so hover styles apply both in the editor (via the class) and on the live site (via the pseudo-class): ```css .testimonial-card.is-hovered, .testimonial-card:hover { /* Add any hover styles here that go beyond what cssProperties covers, such as pseudo-elements or transforms. For simple property changes like color and shadow, statesDefaultValues handles it. No CSS needed. */ } ``` > **Note:** When a Wix user customizes hover values in the design panel, the editor applies them as inline styles on the element. You don't need matching CSS rules for `statesDefaultValues` properties to take effect. For the full reference, see [States](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md). ### What changes in the editor A state selector appears in the design panel. Wix users can switch between "Regular" and "Hover" to style each state independently. ![State selector in the design panel showing Regular and Hover options](https://wixmp-833713b177cebf373f611808.wixmp.com/images/1e3f18389607d96f91c389618bc09aef.png) ## Complete manifest Here's the full manifest with all the properties from the steps above: ```typescript { type: 'myAppCodeIdentifier.testimonialCard', resources: { client: { componentUrl: './extensions/site/components/testimonial-card/component.js', }, }, installation: { presets: { large: 'card', small: 'card', }, initialSize: { width: { sizingType: 'pixels', pixels: 400, }, height: { sizingType: 'content', }, }, }, editorElement: { selector: '.testimonial-card', displayName: 'Testimonial Card', data: { quote: { dataType: 'text', displayName: 'Quote', defaultValue: '"This app transformed our workflow."', text: { maxLength: 500, }, }, authorName: { dataType: 'text', displayName: 'Author Name', defaultValue: 'Jordan Lee', }, authorRole: { dataType: 'text', displayName: 'Author Role', defaultValue: 'Operations Manager', }, image: { dataType: 'image', displayName: 'Author Photo', }, }, cssProperties: { backgroundColor: { defaultValue: '#ffffff', statesDefaultValues: { hover: '#f9fafb', }, }, padding: { defaultValue: '28px', }, borderRadius: { defaultValue: '12px', }, boxShadow: { defaultValue: '0 1px 3px rgba(0,0,0,0.08)', statesDefaultValues: { hover: '0 4px 12px rgba(0,0,0,0.12)', }, }, }, elements: { quoteText: { elementType: 'inlineElement', inlineElement: { selector: '.quote', displayName: 'Quote Text', behaviors: { selectable: true, }, cssProperties: { color: { defaultValue: '#1f2937', }, fontSize: { defaultValue: '16px', }, fontStyle: { defaultValue: 'italic', }, }, }, }, authorNameText: { elementType: 'inlineElement', inlineElement: { selector: '.author-name', displayName: 'Author Name', behaviors: { selectable: false, }, cssProperties: { color: { defaultValue: '#111827', }, fontWeight: { defaultValue: '600', }, fontSize: { defaultValue: '14px', }, }, }, }, authorRoleText: { elementType: 'inlineElement', inlineElement: { selector: '.author-role', displayName: 'Author Role', behaviors: { selectable: false, }, cssProperties: { color: { defaultValue: '#6b7280', }, fontSize: { defaultValue: '13px', }, }, }, }, }, layout: { resizeDirection: 'horizontal', contentResizeDirection: 'vertical', disableStretching: false, }, presets: { card: { displayName: 'Card', thumbnailUrl: 'https://example.com/thumbnails/card-thumbnail.png', presetCssUrl: './extensions/site/components/testimonial-card/presets/card.css', presetDefaults: { cssProperties: { backgroundColor: { defaultValue: '#ffffff' }, borderRadius: { defaultValue: '12px' }, boxShadow: { defaultValue: '0 1px 3px rgba(0,0,0,0.08)' }, }, }, }, minimal: { displayName: 'Minimal', thumbnailUrl: 'https://example.com/thumbnails/minimal-thumbnail.png', presetCssUrl: './extensions/site/components/testimonial-card/presets/minimal.css', presetDefaults: { cssProperties: { backgroundColor: { defaultValue: 'transparent' }, borderRadius: { defaultValue: '0px' }, boxShadow: { defaultValue: 'none' }, }, }, }, }, states: { hover: { displayName: 'Hover', pseudoClass: 'hover', className: 'is-hovered', props: { hovered: true, }, }, }, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [Root Properties Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/root-properties.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About the Manifest ## Article: About the Manifest ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md ## Article Content: # About the Manifest
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The manifest is a TypeScript configuration object for your [Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md). It declares what's customizable about your component, such as editable text, styling, layout, and presets, so the editor can build matching [auto panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/panels/about-auto-panels.md). At runtime, the same manifest defines your component's [prop contract](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md). For the full property reference, see [Root Properties](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/root-properties.md). ## Auto-generated manifest You don't need to write the manifest from scratch. When you [scaffold a component with the CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/editor-react-components/add-an-editor-react-component-extension-with-the-wix-cli.md), the CLI generates the initial manifest files for that scaffolded component. As you iterate on your component, keeping the manifest in sync depends on how you work: - **Manual edits**: The manifest doesn't stay in sync automatically while you edit files yourself. Regenerate it by running [`wix generate manifest`](https://dev.wix.com/docs/wix-cli/command-reference/project-commands/app-only/generate-manifest.md). - **AI edits with the Wix skill**: Manifest regeneration happens as part of the Editor React Component [Wix skill](https://dev.wix.com/docs/wix-cli/command-reference/global-commands/skills-add.md) workflow. ## How the editor uses your manifest Most of your work happens inside `editorElement`, where each property you declare drives a specific [auto panel](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/about-auto-panels.md) or editor behavior. For the per-property reference, see [Editor Element](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md). ## Immutable type properties After you create the manifest for the first time, the following type properties are immutable. Changing any of them from its originally saved value results in a validation error and blocks deployment. To use a different type, add a new variable with the desired name and type instead of modifying the existing one. - `dataType`: The type of a [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). - `elementType`: The type of an [element](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md). - `cssPropertyType`: The type of a [CSS custom property](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md). - `refElement.type`: The type of a [ref element](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md). ## Structural validation constraints The following structural issues result in validation errors: - A required type property is undefined. - A data item with `dataType: "data"` is missing its `data` property or has an empty `items` map. - A data item with `dataType: "arrayItems"` is missing its `arrayItems` property or doesn't define `data`, `dataItem`, or `dynamicItems`. ## Unsupported features The following manifest features aren't currently supported: - **Translations**: Some manifest properties, such as `displayName` in [data items](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md), [presets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md), and [actions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md), as well as the top-level [`resources`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/resources.md) property, support translations. However, the translation mechanism isn't yet available. - **Ref elements**: The [`refElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) element type, which lets you reference and reuse existing Wix components with optional overrides, isn't yet supported. Only [`inlineElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) is available. ## See also - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) - [Runtime Props Reference](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md) - [Tutorial | Configure Auto Panels for an Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/auto-panels/tutorial-configure-auto-panels.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Runtime Props ## Article: Props ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md ## Article Content: # Runtime Props Reference
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    When the editor loads your component, it reads your manifest and passes the current values for each defined property as React props. As Wix users make changes in the editor, your component receives updated props. This page documents the props your component receives at runtime and how they map to your manifest definitions. If you're new to the manifest structure, start with [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md). That page explains what each manifest section configures in the editor, while this page explains how those declarations appear in your runtime component API. Your component receives the following prop categories: - **Data props**: Values from the [`data`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) defined in your manifest. - **Element props**: Data for inner [elements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) defined in your manifest. - **Standard props**: The `className` prop for styling. - **Wix integration props**: The [`wix` object](#wix-integration-props) for editor features. ## Quick mapping from manifest to props Use this as a fast translation layer while implementing your component: - `editorElement.data` maps to top-level props on your component. - `editorElement.elements` maps to nested `elementProps` for inner elements. - Platform-managed integrations map to top-level `className` and optional `wix` props. ## Data props Each key in your manifest's [`data`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) object becomes a prop in your component. The `dataType` you specify determines the runtime type and format of each prop. For example, when you define data types in your manifest: ```typescript data: { title: { dataType: "text", displayName: "Product Name" }, price: { dataType: "number", displayName: "Price" }, productImage: { dataType: "image", displayName: "Product Image" }, inStock: { dataType: "boolean", displayName: "In Stock" } } ``` In your component, each key becomes a prop name with the corresponding runtime type. TypeScript types for all props are available from the [`@wix/editor-react-types`](https://www.npmjs.com/package/@wix/editor-react-types) package: ```typescript import type { Image } from '@wix/editor-react-types'; interface Props { title: string; price: number; productImage: Image; inStock: boolean; } ``` See all available [data types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). ## Element props When you define [elements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) with [data](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) in your manifest, the props for those elements appear in the `elementProps` object. For nested elements, they appear under their parent's `elementProps`. ```typescript interface ElementProps { [dataKey: string]: DataType; elementProps?: Record; } ``` > **Note:** `className` is a top-level prop and will not appear in nested `elementProps`. ### Example A card wrapper component that accesses nested element props: ```tsx interface CardWrapperProps { className: string; listTitle: string; elementProps: { cardItem: CardItemProps; }; } interface CardItemProps { cardTitle: string; } function CardWrapper({ className, listTitle, elementProps }: CardWrapperProps) { const cardItemProps = elementProps.cardItem; return (

    {listTitle}

    ); } ``` Example manifest for this component: ```typescript { type: "CardWrapper", editorElement: { selector: ".card-wrapper", displayName: "Card List", data: { listTitle: { dataType: "text", displayName: "List Title", defaultValue: "Featured Items" } }, elements: { cardItem: { elementType: "inlineElement", inlineElement: { selector: ".card-item", displayName: "Card Item", data: { cardTitle: { dataType: "text", displayName: "Card Title", defaultValue: "Card Title" } } } } } } } ``` ### Nested element props Element props can nest multiple levels deep when elements contain other elements. #### Nested props example A component demonstrating multiple levels of nesting, where child elements have their own configurable properties accessible through nested `elementProps`. ```tsx import type { VectorArt } from '@wix/editor-react-types'; interface Props { elementProps: { card: { cardTitle: string; elementProps: { cardHeader: { headerText: string; headerIcon: VectorArt; }; }; }; }; } function Component({ elementProps }: Props) { const cardProps = elementProps.card; const headerProps = cardProps.elementProps.cardHeader; return (

    {headerProps.headerText}

    {cardProps.cardTitle}

    ); } ``` Example manifest for this component: ```typescript { type: "NestedCard", editorElement: { selector: ".card", displayName: "Card", data: { cardTitle: { dataType: "text", displayName: "Card Title", defaultValue: "Card Title" } }, elements: { cardItem: { elementType: "inlineElement", inlineElement: { selector: ".card-header", displayName: "Card Header", data: { headerText: { dataType: "text", displayName: "Header Text", defaultValue: "Header" }, headerIcon: { dataType: "vectorArt", displayName: "Header Icon" } } } } } } } ``` ## Standard props Every component receives the `className` prop automatically from Wix. This prop enables styling and proper integration with the editor. > **Note:** `className` is a top-level prop only and will not appear in nested `elementProps`. ### `className` CSS class names for your component's root element, including styling from the editor and presets. #### className example Apply `className` to the root element: ```tsx interface ComponentProps { className: string; // ... other props } function MyComponent({ className, ...otherProps }: ComponentProps) { return (
    {/* Component content */}
    ); } ``` ## Wix integration props Your component receives the `wix` prop object from the Wix platform. It includes integration features such as preset styling. ```typescript import type { Wix } from '@wix/editor-react-types'; ``` ```typescript interface Wix { presetsWrapperProps?: { className: string }; } ``` ### Presets wrapper props When your component uses [presets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md), wrap your component in a container element and apply `wix.presetsWrapperProps`. These props contain styling and tracking data for preset functionality. ```typescript type PresetWrapperProps = { className: string } ``` #### Presets wrapper example A button component that uses preset wrapper props to enable preset functionality: ```tsx import type { Wix } from '@wix/editor-react-types'; interface ButtonProps { className: string; wix?: Wix; } function Button({ className, wix }: ButtonProps) { const presetsWrapperProps = wix?.presetsWrapperProps || {}; return (
    ); } ``` ## See also - [Data Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) - [Elements](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) - [Presets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md) - [Tutorial | Configure Auto Panels for an Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/panels/tutorial-configure-auto-panels.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Root Properties ## Article: Root Properties ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/root-properties.md ## Article Content: import { Property, PropertyList } from "@wix/docs-ui/content"; # Root Properties
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Each property in your manifest configures an aspect of how the editor loads, presents, and manages your [Editor React Component](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md). ## Manifest properties A unique identifier for the component in the format 'codeIdentifier.componentName'. Find your app's code.md identifier in the app dashboard, and choose any name for 'componentName'. The editor uses this value to identify which component to render when a Wix user adds it to their site.} /> Resources}.md required description="Declares the code and assets for your component: client and editor bundles, CSS, server rendering, SDK, translations, and animation configuration." /> Installation}.md description="Controls initial sizing, preset selection per breakpoint, and placement when a Wix user first adds the component." /> EditorElement}.md required description="The core of the manifest. Defines the component's root selector, display name, and all configurable properties including data, styling, layout, presets, states, and more." /> ## Examples ### Minimal manifest A manifest with only the required properties. The component renders in the editor but doesn't expose any customization controls. ```typescript { type: 'myApp.myWidget', resources: { client: { componentUrl: './extensions/site/components/my-widget/component.tsx', }, }, editorElement: { selector: '.my-widget', displayName: 'My Widget', }, } ``` ### Manifest with data and design controls A manifest that exposes editable text and image content, plus design controls for background color and padding. ```typescript { type: 'myApp.profileCard', resources: { client: { componentUrl: './extensions/site/components/profile-card/component.tsx', }, }, editorElement: { selector: '.profile-card', displayName: 'Profile Card', data: { name: { dataType: 'text', displayName: 'Name', defaultValue: 'Jane Doe', }, photo: { dataType: 'image', displayName: 'Photo', }, }, cssProperties: { backgroundColor: { defaultValue: '#ffffff' }, padding: { defaultValue: '24px' }, }, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Installation ## Article: Installation ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/installation.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Installation
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `installation` property controls how your component appears when a Wix user first adds it to a page, including its initial dimensions, which preset it starts with, and where it's placed. It works together with [`layout`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout.md): `installation` sets the starting size, and `layout` controls how the Wix user can resize from there. ## Where to define You can only define `installation` at the root level of your manifest, alongside properties like `type` and `editorElement`. See the [manifest structure](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/root-properties.md). ## Installation properties The `installation` object supports the following properties: Specify different presets for different screen sizes to ensure your component adapts appropriately. The values must match keys defined in your component's [presets](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md) configuration. ## Initial size The `initialSize` property controls the starting dimensions when a Wix user first adds your component to a page. Each dimension (`width` and `height`) is configured independently with a sizing type. ## Examples ### Fixed width with content-driven height The component has a set width but grows vertically to fit its content. This is the most common pattern, used by text components, accordions, and tabs. ```typescript installation: { initialSize: { width: { sizingType: 'pixels', pixels: 400, }, height: { sizingType: 'content', }, }, } ``` ### Full-width with content-driven height The component stretches to fill its parent section while the height adjusts to content. Useful for banners, marquees, and full-width layouts. ```typescript installation: { initialSize: { width: { sizingType: 'stretched', }, height: { sizingType: 'content', }, }, } ``` ### Fully content-driven Both dimensions adjust to the component's content. This works for components with an intrinsic size, like icon bars or badges. ```typescript installation: { initialSize: { width: { sizingType: 'content', }, height: { sizingType: 'content', }, }, } ``` ### Fixed dimensions with aspect ratio lock The component maintains its width-to-height ratio when the user resizes it. Use `preserveAspectRatio` only when both dimensions use `'pixels'` sizing. Primarily useful for media components like images, videos, and logos. ```typescript installation: { initialSize: { width: { sizingType: 'pixels', pixels: 320, }, height: { sizingType: 'pixels', pixels: 240, }, preserveAspectRatio: true, }, } ``` ### Different presets for desktop and mobile ```typescript { installation: { initialSize: { width: { sizingType: 'pixels', pixels: 320 }, height: { sizingType: 'pixels', pixels: 240 }, }, presets: { large: 'desktopCard', small: 'mobileCard', }, }, editorElement: { presets: { desktopCard: { displayName: 'Desktop Layout', presetCssUrl: './presets/desktop.css', initialSize: { width: { sizingType: 'pixels', pixels: 600 }, height: { sizingType: 'pixels', pixels: 400 }, }, }, mobileCard: { displayName: 'Mobile Layout', presetCssUrl: './presets/mobile.css', initialSize: { width: { sizingType: 'pixels', pixels: 300 }, height: { sizingType: 'pixels', pixels: 200 }, }, }, }, }, } ``` ## See also - [Layout](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout.md) - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Resources ## Article: Resources Table ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/resources.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Resources
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `resources` property declares the code and assets that make up your component. When you build with the [CLI](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-cli/about-the-wix-cli.md), it handles resource management automatically: bundling your code, uploading assets, and generating the resource URLs. ## Where to define You can only define `resources` at the [root level of your manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/root-properties.md), alongside properties like `type` and `editorElement`. The `resources` property is required, and at minimum, you need `client.componentUrl`. ## Resources properties The `resources` object supports the following properties:
    __Important:__ Your server bundle must avoid browser-specific APIs like `window` and `document`. If your component relies on these APIs, provide a fallback that renders static content on the server.
    ## Examples ### Minimal resources The simplest configuration, with only the required `client.componentUrl`: ```typescript import componentUrl from './component.tsx?url'; resources: { client: { componentUrl, }, } ``` ### Client with default CSS A client bundle with a CSS file. The CSS is rendered once per page, regardless of how many instances of the component exist: ```typescript import componentUrl from './component.tsx?url'; import cssUrl from './styles.module.css?url'; resources: { client: { componentUrl, cssUrl, }, } ``` ### Client and editor bundles Separate bundles for the live site and the editor: ```typescript import componentUrl from './component.tsx?url'; import cssUrl from './styles.module.css?url'; import editorComponentUrl from './editor-component.tsx?url'; import editorCssUrl from './editor-styles.module.css?url'; resources: { client: { componentUrl, cssUrl, }, editor: { componentUrl: editorComponentUrl, cssUrl: editorCssUrl, }, } ``` ### Full resources configuration A complete `resources` object with all supported properties: ```typescript import componentUrl from './component.tsx?url'; import cssUrl from './styles.module.css?url'; import editorComponentUrl from './editor-component.tsx?url'; import editorCssUrl from './editor-styles.module.css?url'; import serverUrl from './server.tsx?url'; import sdkUrl from './sdk.ts?url'; resources: { client: { componentUrl, cssUrl, moduleSpecifier: '@wix/my-component', }, editor: { componentUrl: editorComponentUrl, cssUrl: editorCssUrl, moduleSpecifier: '@wix/my-component-editor', }, server: { url: serverUrl, }, sdk: { url: sdkUrl, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) - [States](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Editor Element ## Article: Editor Element ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Editor Element
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `editorElement` object is the core of the manifest. It defines the component's root selector and display name, and declares everything that should be customizable from data and styling to layout, presets, and states. Each property you define in `editorElement` automatically generates editor UI, so you don't need to build settings panels yourself. For a summary of what each property generates, see [About Auto Panels](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/panels/about-auto-panels.md). ## Where to define You can only define `editorElement` at the root level of your manifest, alongside properties like `type` and `resources`. ## Editor element properties The `editorElement` object supports the following properties: map<string, DataItem>}.md description="Data properties for the component. Each key is a data item name and each value is a data item definition. Passed to the component as props at runtime." /> map<string, CssPropertyItem>}.md description="Standard CSS properties applied as overrides in the scope of the component. Each key is a CSS property name and each value is the property configuration." /> map<string, CssCustomPropertyItem>}.md description="CSS custom properties (variables) applied in the scope of the component. Each key is the custom property name and each value is the property configuration." /> map<string, ElementItem>}.md description="Inner elements of the component with their own data, styling, and behaviors. Each key is a unique element identifier." /> Actions}.md description="Override default editor actions for the component. Optional, and not recommended if you want the standard editor experience." /> map<string, CustomAction>}.md description="Developer-defined custom actions for the component. Each key is a unique action identifier. Use for custom behaviors or integration with external domains." /> map<string, PresetItem>}.md description="Named visual variations of the component, each with an optional CSS file. Complementary to the default CSS file in resources. Each key is a unique preset key." /> Layout}.md description="Layout capabilities of the component, including resize behavior, content sizing, and positioning." /> map<string, StateItem>}.md description="Conditions like hover or disabled that change how the component looks based on user interactions." /> DisplayFilters}.md description="Control which properties, elements, and actions appear in the editor panels. Applied first, before preset and state filters." /> Archetype}.md description="Semantic classification of the component for AI tools and editor behaviors." /> Interactions}.md description="Triggers and effect groups for declarative animations that Wix users can configure." /> map<string, DisplayGroupItem>}.md description="Group related properties together in the editor for better organization. Each key is a unique display group identifier." /> ## Examples ### Button with data, CSS properties, and presets The following defines a button component with editable text, a style variant dropdown, a link, and a disabled toggle. It exposes background color, text color, and padding as design controls, a custom border radius variable, horizontal-only resizing, two visual presets, and a custom action that opens a panel. ```typescript editorElement: { selector: '.custom-button', displayName: 'Custom Button', archetype: 'Button', data: { text: { dataType: 'text', displayName: 'Button Text', defaultValue: 'Click me', text: { minLength: 1, maxLength: 50, }, }, variant: { dataType: 'textEnum', displayName: 'Button Animation Style', defaultValue: 'primary', textEnum: { options: [ { value: 'primary', displayName: 'Primary' }, { value: 'secondary', displayName: 'Secondary' }, { value: 'outline', displayName: 'Outline' }, ], }, }, disabled: { dataType: 'booleanValue', displayName: 'Disabled', defaultValue: 'false', }, link: { dataType: 'link', displayName: 'Button Link', link: { linkTypes: ['externalLink'], }, }, }, cssProperties: { backgroundColor: { displayName: 'Background Color', defaultValue: '#007bff', }, color: { displayName: 'Text Color', defaultValue: '#ffffff', }, padding: { displayName: 'Padding', defaultValue: '12px 24px', }, }, cssCustomProperties: { borderRadius: { cssPropertyType: 'number', displayName: 'Border Radius', defaultValue: 4, number: { minimum: 0, maximum: 50, }, }, }, layout: { resizeDirection: 'horizontal', contentResizeDirection: 'none', }, presets: { cta: { displayName: 'Call to Action', description: 'Large prominent button for primary actions', cssFile: 'https://somecdn.com/my-button-assets/button-cta.css', }, minimal: { displayName: 'Minimal', description: 'Clean minimal button style', cssFile: 'https://somecdn.com/my-button-assets/button-minimal.css', }, }, customActions: { openModal: { displayName: 'Open Modal', description: 'Opens a custom modal dialog', execution: { actionType: 'panel', panel: { panelType: 'panelId', panelId: 'a1b2c3d4-e5f6-4a5b-8c7d-9e0f1a2b3c4d', }, }, }, }, } ``` ### Card with display groups The following defines a card component that uses `displayGroups` to organize its properties into logical sections in the editor panels. Border-related CSS properties are grouped together, CSS custom properties for visual effects are grouped separately, and data items for content are in their own group. ```typescript editorElement: { selector: '.advanced-card', displayName: 'Advanced Card', cssProperties: { backgroundColor: { displayName: 'Background Color', defaultValue: '#ffffff', }, borderWidth: { displayName: 'Border Width', defaultValue: '1px', }, borderStyle: { displayName: 'Border Style', defaultValue: 'solid', }, borderColor: { displayName: 'Border Color', defaultValue: '#e0e0e0', }, }, cssCustomProperties: { shadowIntensity: { cssPropertyType: 'number', displayName: 'Shadow Intensity', defaultValue: 0.1, }, animationSpeed: { cssPropertyType: 'number', displayName: 'Animation Speed', defaultValue: 0.3, }, }, data: { title: { dataType: 'text', displayName: 'Card Title', }, subtitle: { dataType: 'text', displayName: 'Card Subtitle', }, }, displayGroups: { borderSettings: { displayName: 'Border Settings', groupType: 'border', border: { width: 'borderWidth', style: 'borderStyle', color: 'borderColor', }, }, visualEffects: { displayName: 'Visual Effects', groupType: 'cssDataType', cssDataType: { items: ['shadowIntensity', 'animationSpeed'], }, }, contentData: { displayName: 'Content', groupType: 'dataType', dataType: { items: ['title', 'subtitle'], }, }, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) - [About Editor React Components](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/about-editor-react-components.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Archetype ## Article: Archetype ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/archetype.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Archetype
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `archetype` property is a semantic classification that tells the editor what kind of component you're building. The editor and AI tools use this hint to provide appropriate default behaviors, suggestions, and capabilities for your component. ## Where to define You can define `archetype` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`. ## Archetype values ## Examples ### Button component ```typescript editorElement: { selector: '.my-button', displayName: 'My Button', archetype: 'Button', } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Display Filters ## Article: Display Filters ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Display Filters
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `displayFilters` property controls which properties appear in the editor panels when Wix users select your component. Use it to hide properties that your component uses internally but that Wix users shouldn't edit, or to show only relevant properties for a specific preset or state. ## Where to define You can define `displayFilters` in: - [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) {/* TODO: when refElements are supported, add here */} - [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement` - [Preset](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md) items (inside `presetDefaults`) - [State](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) items If multiple levels define filters, the editor applies them in the following order: 1. Element filters 2. Preset filters 3. State filters Only items that pass every applicable filter appear in the editor. ## Display filter keys Each key corresponds to a section in your manifest. Use `hide` or `show` (not both) to control which items from that section appear in the editor. If you don't define a display filter for a category, no filter is applied. - `hide`: The editor shows all items except those in the list. - `show`: The editor shows only the items in the list, everything else is hidden.
    ## Relationship with display groups [Display groups](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-groups/display-groups.md) and display filters operate independently. Display filters remove properties before the editor panel receives them. Display groups organize whatever properties remain. ## Examples ### Element and preset filters The following uses both `hide` and `show` across levels. At the component level, the component hides the data property `internalId`. The `'compact'` preset then uses `show` to restrict the visible data properties to just `title` and `icon`. When you select that preset, only those two appear in the editor; the editor hides `subtitle` and any other data properties. ```typescript editorElement: { displayFilters: { data: { hide: ['internalId'] }, }, presets: { compact: { displayName: 'Compact', presetDefaults: { displayFilters: { data: { show: ['title', 'icon'] }, }, }, }, }, } ``` ### All supported filter keys The following shows `displayFilters` with all supported keys configured. ```typescript editorElement: { displayFilters: { elements: { hide: ['advancedIcon', 'debugPanel'], }, cssProperties: { show: ['backgroundColor', 'color', 'fontSize', 'padding', 'margin'], }, cssCustomProperties: { show: ['borderRadius', 'shadowDepth'], }, data: { hide: ['internalConfig', 'debugMode', 'advancedSettings'], }, customActions: { show: ['editContent', 'duplicateElement', 'resetStyles'], }, actions: { hide: ['dashboard', 'manageItems'], }, states: { hide: ['open', 'loading'], }, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Elements ## Article: Elements ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Elements
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `elements` property lets you make inner parts of your component independently selectable and configurable in the editor. Each element gets its own selector, display name, and optional data, styling, states, and actions. When a Wix user clicks on that part of your component, they see controls specific to it rather than the whole component. For example, a product card with a title, image, and buy button could define each part as a separate element. Clicking the button in the editor opens controls for just that button, such as its text, link, colors, and states, without affecting the rest of the card. ## Where to define {/* TODO: when refElements are supported, add here */} You can define `elements` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in other `elements` of type `inlineElement`.
    **Important:** Elements can nest multiple levels deep. When an element defines `data`, those values are passed to your component inside the `elementProps` object rather than as top-level props. Nested elements appear under their parent's `elementProps`. For more information, see [Props](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md).
    ## Element item Each entry in the `elements` map is an element item. It specifies the element's type and contains the corresponding configuration object. The element type. Supported values: inlineElement or refElement. Only inlineElement is currently supported.} /> InlineElement} description="Configuration for the element when elementType is 'inlineElement'." /> RefElement} description="Configuration for the element when elementType is 'refElement'. Not yet supported." /> ## Inline element An inline element is a custom element you define from scratch with its complete configuration. map<string, DataItem>}.md description="Data properties for this element, passed as props to your component." /> Actions}.md description="Native platform actions available for this element." /> map<string, CustomAction>} description="Developer-defined custom actions specific to your component." /> map<string, PresetItem>} description="Predefined style or layout variations." /> Archetype}.md description="Semantic classification hint for AI and editor features." /> map<string, ElementState>} description="Define component states for preview and testing." /> VisibleState} description="Links element visibility to a component state for conditional display." /> DisplayFilters}.md description="Control visibility of properties in the editor panel." /> map<string, CssPropertyItem>}.md description="Direct CSS properties applied to the element." /> map<string, CssCustomPropertyItem>}.md description="CSS custom properties declared with names prefixed by `--`, for reusable values referenced with `var()`." /> ## Ref element
    **Caution:** Support for ref elements is under development. See [Unsupported features](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md#unsupported-features).
    A ref element references an existing Wix component and optionally overrides its properties. Actions}.md description="Override or hide native actions from the referenced component." /> map<string, CustomAction>} description="Override custom actions from the referenced component." /> VisibleState} description="Links element visibility to a component state for conditional display." /> DisplayFilters}.md description="Control visibility of properties in the editor panel." /> ## Examples ### Progress bar with nested elements A progress bar with CSS custom properties and non-selectable sub-elements. ```typescript editorElement: { selector: '.progress-container', displayName: 'Progress Bar', data: { value: { dataType: 'number', displayName: 'Progress', defaultValue: 60, }, }, elements: { label: { elementType: 'inlineElement', inlineElement: { selector: '.progress-label', displayName: 'Label', behaviors: { selectable: false, removable: true, }, data: { text: { dataType: 'text', displayName: 'Label Text', defaultValue: 'Progress', }, }, cssProperties: { color: { displayName: 'Text Color', }, }, }, }, bar: { elementType: 'inlineElement', inlineElement: { selector: '.progress-bar', displayName: 'Bar', behaviors: { selectable: false, removable: false, }, cssCustomProperties: { barColor: { displayName: 'Bar Color', cssPropertyType: 'color', defaultValue: '#3899EC', }, barBorderRadius: { displayName: 'Bar Corner Radius', cssPropertyType: 'number', defaultValue: '4px', }, }, }, }, }, } ``` ### Removable element with display toggle A button component with a removable icon element. Setting `removable: true` lets Wix users toggle the element on or off. The `display` CSS property with `displayValues` controls which display states are available when the element is visible. ```typescript editorElement: { selector: '.button', displayName: 'Button', elements: { icon: { elementType: 'inlineElement', inlineElement: { selector: '.button-icon', displayName: 'Icon', behaviors: { selectable: false, removable: true, }, cssProperties: { display: { displayValues: ['none', 'inline-block'], }, }, }, }, }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Interactions ## Article: Interactions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/interactions.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Interactions
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `interactions` property controls which animation options appear in the editor's animation panel. It's configured with the following 2 concepts: - **Triggers**: Define when an animation starts, such as when the component enters the viewport or as the site visitor scrolls. - **Effect groups**: Define what animates, such as just the background layer while the content stays static. ## Where to define You can define `interactions` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`. ## Interactions properties Each `interactions` object accepts the following properties: Each trigger corresponds to a specific type of animation experience. ## Examples ### Full interaction support Use when you want to give Wix users access to all animation types. Good default for most components. ```typescript editorElement: { interactions: { triggers: [ 'viewEnter', 'pageVisible', 'animationEnd', 'viewProgress', 'pointerMove', ], }, } ``` ### Entrance and scroll only Use for components that should animate in and respond to scrolling, but don't need looping or mouse-tracking. Good for static content like headings, cards, or badges. ```typescript editorElement: { interactions: { triggers: [ 'viewEnter', 'viewProgress', ], }, } ``` ### Container with background effects Use for sections or wrappers where the background should animate independently of the content inside. The `'background'` effect group targets just the background layer. ```typescript editorElement: { archetype: 'Container', interactions: { triggers: [ 'viewEnter', 'pageVisible', 'animationEnd', 'viewProgress', 'pointerMove', ], effectGroups: ['background'], }, data: { children: { dataType: 'container', container: { selector: '.content', containerType: 'simple', simple: {}, }, }, }, } ``` ### Media component with background effects Use for image or video components where the media itself should animate. The `'background'` effect group applies effects to the media layer. ```typescript editorElement: { archetype: 'Image', interactions: { triggers: [ 'viewEnter', 'pageVisible', 'animationEnd', 'pointerMove', 'viewProgress', ], effectGroups: ['background'], }, } ``` ## See also - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Layout ## Article: Layout ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/layout.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Layout
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `layout` property controls resize directions, content sizing, stretch, positioning, rotation, and duplication behavior for your component in the editor. It works together with [`installation.initialSize`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/installation.md), which sets the starting dimensions, and `layout` controls how the Wix user can change them from there. ## Where to define You can define `layout` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md), or override it per preset inside [`presetDefaults.layout`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md).
    **Caution:** You can only apply `layout` to the root `editorElement`, not to [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md).
    ## Layout properties The `layout` object supports the following fields: Controls which directions the Wix user can manually resize the component. This affects resize handles and editing capabilities using AI. Use `contentFill` when your component displays content that fills its entire visible area, such as an image, video, or background. This tells the editor which data item provides that content, enabling features like intelligent cropping.
    **Caution:** When `contentResizeDirection` includes a direction, the editor sizes the component's container to fit its content in that direction. This means your component's CSS can't use parent-relative values like `height: 100%` for that dimension, because the parent itself has no fixed size. Without a fixed value or content that takes up space, the component may collapse to zero.
    ## Common patterns The patterns below show how different combinations of `resizeDirection` and `contentResizeDirection` work for common component types. ### Text block The Wix user sets the width in the editor, and the height grows automatically as content wraps. ```json { "editorElement": { "layout": { "resizeDirection": "horizontal", "contentResizeDirection": "vertical", "disableStretching": true } } } ``` This pattern also works for progress bars, inline labels, and single-line inputs. ### Image The Wix user can resize freely in the editor. The image scales to fill the component without pushing its boundaries out, and `contentFill` enables the editor's cropping tools. ```json { "editorElement": { "layout": { "resizeDirection": "horizontalAndVertical", "contentResizeDirection": "none", "contentFill": { "dataItemKey": "image" } } } } ``` This pattern also works for videos, background media, and maps. ### Logo Resizing preserves the aspect ratio. The image fills the component but doesn't auto-resize. ```json { "editorElement": { "layout": { "resizeDirection": "aspectRatio", "contentResizeDirection": "none", "disableStretching": true, "contentFill": { "dataItemKey": "image" } } } } ``` This pattern also works for icons, avatars, and thumbnails. ### Button The Wix user can resize the component in both directions, while the height adjusts automatically to fit the content, such as the label, icon, or spacing. ```json { "editorElement": { "layout": { "resizeDirection": "horizontalAndVertical", "contentResizeDirection": "vertical", "disableStretching": true } } } ``` This pattern also works for tags, chips, and call-to-action banners. ### Container The Wix user controls the height in the editor. Children fill the component's visible area. To make the component span its parent's width on install, set `installation.initialSize.width.sizingType` to `'stretched'`. For details, see [`installation`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/root-properties/installation.md). ```json { "editorElement": { "layout": { "resizeDirection": "vertical", "contentFill": { "dataItemKey": "children" } } } } ``` This pattern also works for sections, panels, card groups, and sidebars. ### Fixed-size element The Wix user can't resize the component. Its content or fixed dimensions entirely determine the component's size. ```json { "editorElement": { "layout": { "resizeDirection": "none", "contentResizeDirection": "none" } } } ``` This pattern also works for badges, status indicators, and separators. --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: States ## Article: States ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # States
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `states` property lets you define conditions that Wix users can style independently in the editor, such as hover, disabled, and loading. Each key in the `states` map is the **state key**, and you use it to reference the state in `statesDefaultValues`, [display filters](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md), and [style overrides](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md). ## Where to define {/* TODO: when refElements are supported, add here */} You can define `states` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`.
    **Tip:** You can filter which states appear in the editor using [`displayFilters`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md) at the preset or element level.
    ## CSS style defaults for states To set default CSS values that the design panel shows when a state is active, use `statesDefaultValues` on your `cssProperties` or `cssCustomProperties` items. The key names must match state keys defined in the `states` map. See the [`statesDefaultValues` example](#statesdefaultvalues) below. ## State item properties Each item in the `states` map accepts the following properties: Applied when a user's pointer is over the element. Maps to CSS :hover.} /> Applied when the element has received focus. Maps to CSS :focus.} /> Applied when the element is disabled. Maps to CSS :disabled.} /> Applied when the element's value fails validation. Maps to CSS :invalid.} /> DisplayFilters}.md description="Controls which CSS properties, elements, data items, and custom actions are visible in the editor when this state is active. Applied last, after element and preset filters." /> ## Visible state The `visibleState` property is defined on elements to control an element's visibility based on a state defined on another element. When used, both fields are required. ## Examples ### statesDefaultValues Setting default CSS values per state. When a Wix user selects the hover state in the design panel, `backgroundColor` shows `#f0f0f0` instead of `#ffffff`. ```typescript editorElement: { cssProperties: { backgroundColor: { defaultValue: '#ffffff', statesDefaultValues: { hover: '#f0f0f0', }, }, }, states: { hover: { displayName: 'Hover' }, }, } ``` ### Open dropdown state The following defines a state that opens a dropdown so that its inner elements are visible on stage. ```typescript editorElement: { states: { openDropDown: { displayName: 'Open Drop Down', props: { open: true, }, }, }, } ``` ### Loading state with className The following defines a loading state with a `className` for styling and `props` to activate it on stage. ```typescript editorElement: { states: { loadingState: { displayName: 'Loading Data', className: 'loading', props: { loading: true, }, }, }, } ``` ### Hover state with pseudoClass The following defines a hover state. The `pseudoClass` handles `:hover` on the live site, while the `props` field allows the component to apply hover styles on the editor stage. Your CSS must target both. ```typescript editorElement: { states: { hover: { displayName: 'Hover', className: 'elementHover', pseudoClass: 'hover', props: { hovered: true, }, }, }, } ``` ```css .element.elementHover, .element:hover { /** YOUR CSS FOR HOVER STATE */ } ``` ### Display filters The following defines a button hover state that only shows specific CSS properties and hides certain data items when the state is selected. ```typescript editorElement: { states: { buttonHover: { displayName: 'Button Hover', className: 'btn-hover-state', pseudoClass: 'hover', props: { variant: 'primary', size: 'large', disabled: false, loading: false, }, displayFilters: { cssProperties: { show: ['backgroundColor', 'borderColor', 'color', 'transform'], }, data: { hide: ['advancedOptions', 'debugSettings'], }, }, }, }, } ``` ### Visible state The following shows a `visibleState` configuration on an inner element, making it visible only when the `showAdvanced` state is active on the element at `/settingsPanel/advancedOptions`. ```typescript editorElement: { elements: { myElement: { elementType: 'inlineElement', inlineElement: { selector: '.my-element', displayName: 'My Element', visibleState: { stateKey: 'showAdvanced', elementPath: '/settingsPanel/advancedOptions', }, }, }, }, } ``` ### States with CSS style defaults The following defines a `border` property with a dashed green default and a solid red override for the error state. ```typescript editorElement: { cssProperties: { border: { displayName: 'Main component Border', defaultValue: 'dashed 2px green', statesDefaultValues: { error: 'solid 2px red', }, }, }, states: { error: { displayName: 'Error', }, }, } ``` ## See also - [States](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Presets ## Article: Presets ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/presets.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Presets
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `presets` property lets you define named visual variations of your component. Each key in the `presets` map is the preset's unique identifier, which is referred to as the preset key throughout this documentation. Each value configures the preset's display name, thumbnail, sizing, default values, and optional style overrides. ## Where to define You can define `presets` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md).
    **Important:** For presets to work, your component must wrap its root element in a container and spread `wix.presetsWrapperProps` onto it.
    ## Preset defaults vs. style overrides Each preset item has 2 fields that control its appearance: `presetDefaults` and `styleOverrides`. Both are optional, and they serve different purposes: - Use `presetDefaults` to define the initial values that apply when a Wix user switches to a preset. This includes layout behavior, CSS properties, CSS custom properties, display filters, and nested element defaults. These values replace the component's base configuration for that preset. - Use `styleOverrides` when you need breakpoint-aware or state-aware value overrides. For example, you might use `styleOverrides` to set a different hover color on mobile, or to adjust font sizes across breakpoints. Overrides in the `default` breakpoint cascade to all other breakpoints, while `small` overrides apply only to the mobile breakpoint. ## Preset item properties Each item in the `presets` map accepts the following properties: Sets default values for nested elements in a preset. This schema is recursive, so each inner element can contain its own nested `elements`. EditorElementLayout}.md description="Layout behaviors to apply when this preset is selected." /> DisplayFilters}.md description="Controls visibility of elements, properties, and actions for this preset." /> ## Installation presets The `installation.presets` object maps breakpoints to preset keys, controlling which preset the component starts with when first added to the page. ## Examples ### Basic presets The following defines 2 presets for a line component, 1 horizontal and 1 vertical, each with different initial sizes and layout defaults. ```typescript editorElement: { presets: { horizontal: { displayName: 'Horizontal', initialSize: { width: { sizingType: 'pixels', pixels: 280, }, height: { sizingType: 'content', }, }, presetDefaults: { layout: { resizeDirection: 'horizontal', contentResizeDirection: 'vertical', disableStretching: false, disablePositioning: false, }, }, }, vertical: { displayName: 'Vertical', initialSize: { width: { sizingType: 'content', }, height: { sizingType: 'pixels', pixels: 280, }, }, presetDefaults: { layout: { resizeDirection: 'vertical', contentResizeDirection: 'horizontal', disableStretching: false, disablePositioning: false, }, }, }, }, } ``` ### Preset element defaults The following configures layout, CSS property defaults, and display filters for a menu preset. The `displayFilters` object controls which elements, actions, and properties appear in the editor for this preset. In this case, it shows the `navbar` element and hides the `manageMenu` action. ```typescript editorElement: { presets: { menuPreset: { presetDefaults: { layout: { resizeDirection: 'horizontal', contentResizeDirection: 'horizontal', disableStretching: false, disablePositioning: false, disableRotation: true, }, cssProperties: { writingMode: { defaultValue: 'horizontal-tb', }, }, displayFilters: { elements: { show: ['navbar'], }, actions: { hide: ['manageMenu'], }, }, }, }, }, } ``` ### Inner element defaults The following configures CSS custom property defaults for a nested `line` element. ```typescript editorElement: { presets: { myPreset: { presetDefaults: { elements: { line: { cssCustomProperties: { type: { defaultValue: 'solid', }, lineColor: { defaultValue: '#000000', }, lineSize: { defaultValue: '1px', }, }, }, }, }, }, }, } ``` ### Component initial size The following sets the initial width to 120 pixels and the height to fit the content. ```typescript editorElement: { presets: { myPreset: { initialSize: { width: { sizingType: 'pixels', pixels: 120, }, height: { sizingType: 'content', }, }, }, }, } ``` ### Installation presets The following sets the horizontal preset for both breakpoints and defines a default initial width of 280 pixels. ```typescript installation: { presets: { default: 'horizontal', small: 'horizontal', }, initialSize: { width: { sizingType: 'pixels', pixels: 280, }, height: { sizingType: 'content', }, }, } ``` ### Style overrides The following overrides background color with state-specific hover values at the default breakpoint, and adjusts font size for mobile. ```typescript editorElement: { presets: { myPreset: { styleOverrides: { default: { style: { backgroundColor: { value: '#ffffff', statesValues: { hover: '#f0f0f0', }, }, fontSize: { value: '16px', }, }, elements: { menuItem: { style: { padding: { value: '12px 16px', }, }, }, }, }, small: { style: { fontSize: { value: '14px', }, }, elements: { menuItem: { style: { padding: { value: '8px 12px', }, }, }, }, }, }, }, }, } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Native and Custom Actions ## Article: Native and Custom Actions ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/native-and-custom-actions.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Native and Custom Actions
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `actions` and `customActions` properties control the toolbar that appears when a Wix user selects your component in the editor. ## Where to define {/* TODO: when refElements are supported, add here */} You can define `actions` and `customActions` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`. - **Native actions** are derived from your component's manifest and generated automatically by the editor. For example, `data` items produce a `settings` action, and `cssProperties` produce a `design` action. If you don't define an `actions` object at all, the editor generates these for you. You can override them using the `actions` property. - **Custom actions** are additional buttons you define using the `customActions` property. They appear as labeled text buttons before the icon-based native actions. Each action is a key-value pair. The key is the action's identifier, and the value is an object. ## Native actions Native actions are defined using the `actions` property. All fields are optional since the editor provides defaults. Defining `execution` on a native action overrides its default behavior. Label shown in the action bar. 4 to 20 characters. Translatable. Required for custom actions.} /> Execution}.md description={<>Defines what happens when the action is clicked. Required for custom actions.} /> Label shown in the action bar. 4 to 20 characters. Translatable. Required for custom actions.} /> Execution}.md description={<>Defines what happens when the action is clicked. Required for custom actions.} /> Label shown in the action bar. 4 to 20 characters. Translatable. Required for custom actions.} /> Execution}.md description={<>Defines what happens when the action is clicked. Required for custom actions.} /> Label shown in the action bar. 4 to 20 characters. Translatable. Required for custom actions.} /> Execution}.md description={<>Defines what happens when the action is clicked. Required for custom actions.} /> Unlike the other native actions, the `dashboard` action opens a dashboard page extension on top of the editor. You define which page to open using the `dashboardPageId` field. If omitted, the default dashboard page opens. Execution}.md description="Defines what happens when the action is clicked." />
    > **Note:** Additional native actions, such as `presets` for components with templates, may also appear depending on your component's capabilities. ## Custom actions Custom actions are defined using the `customActions` property. Each custom action requires a `displayName` and an `execution`. For the full list of execution types, see [Execution Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/execution-types.md). Custom actions can be defined at the root `editorElement` level or on elements in the `elements` map, allowing child elements to have their own dedicated actions. ### Examples #### Hide a native action The following example hides the media action from the action bar: ```json { "editorElement": { "actions": { "media": { "hidden": true } } } } ``` #### Override display names The following example overrides the display name of the settings and design actions: ```json { "editorElement": { "actions": { "settings": { "displayName": "Slideshow Settings", "execution": { "actionType": "data", "data": { "dataItemKey": "slides" } } }, "design": { "displayName": "Slideshow Design", "execution": { "actionType": "cssProperty", "cssProperty": { "cssPropertyKey": "backgroundColor" } } } } } } ``` #### Redirect manageItems The following example redirects `manageItems` to target the `slides` array by providing an `execution` object: ```json { "editorElement": { "actions": { "manageItems": { "displayName": "Manage Slides", "execution": { "actionType": "data", "data": { "dataItemKey": "slides" } } } } } } ``` For the full list of execution types, see [About Execution Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/execution-types.md). #### Dashboard action The following example opens a specific dashboard page when the user clicks the action: {/* This example is used in original documentation. Dev raised question in PR - if it has both dashboard page id and panel execution, which wins/ takes precedence? */} ```json { "editorElement": { "actions": { "dashboard": { "hidden": false, "displayName": "Analytics Dashboard", "dashboardPageId": "b2c3d4e5-f6a7-5b6c-9d8e-0f1a2b3c4d5e", "execution": { "actionType": "panel", "panel": { "panelType": "panelId", "panelId": "c3d4e5f6-a7b8-6c7d-0e9f-1a2b3c4d5e6f" } } } } } } ``` #### Custom action with panel The following example adds a "Change Colors" button that opens a panel: ```json { "editorElement": { "customActions": { "vectorArtColors": { "displayName": "Change Colors", "execution": { "actionType": "panel", "panel": { "panelType": "panelId", "panelId": "c0b164d9-cd88-41d3-a45e-51b14c54c3ea" } } } } } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Execution Types ## Article: Execution Types ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/execution-types.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Execution Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Every action (native or custom) can specify an `execution` object that tells the editor what happens when the user clicks the action. The `execution` object contains an `actionType` field that determines the behavior, and a corresponding payload. ## Execution properties Each `execution` object accepts the following properties: The `elementPath` must start with `.root` or `.self`. Each segment after the prefix corresponds to a key in the `elements` map of that level of the manifest. Set `actionName` to any native action name, or to `custom` for custom actions. > **Note:** Less common action names include `manageMenu`, `help`, `plugins`, `presets`, `interactions`, and more. ## Examples ### Data action The following example opens the data-editing UI for the `slides` data item: ```json { "actionType": "data", "data": { "dataItemKey": "slides" } } ``` ### Panel action with panelId The following example opens a panel registered in the app dashboard: ```json { "actionType": "panel", "panel": { "panelType": "panelId", "panelId": "c0b164d9-cd88-41d3-a45e-51b14c54c3ea" } } ``` ### Inline panel from URL The following example opens an inline panel that loads content from a URL: ```json { "actionType": "panel", "panel": { "panelType": "inline", "inline": { "displayName": "Color Settings", "contentType": "url", "url": "https://example.com/my-panel.html", "size": { "height": 300, "width": "MEDIUM" } } } } ``` ### Inline panel from code The following example opens an inline panel rendered from a React component: ```json { "actionType": "panel", "panel": { "panelType": "inline", "inline": { "displayName": "Advanced Settings", "contentType": "code", "code": { "bundleUrl": "https://example.com/panel-bundle.js", "cssUrl": "https://example.com/panel-styles.css" }, "panel_mode": "overlay" } } } ``` ### Forward action The following example forwards a custom action to a child element. The target element (`previewContainer`) must define a `customActions.togglePreview` entry in its own manifest for this to work. ```json { "actionType": "forward", "forward": { "elementPath": ".root.previewContainer", "actionName": "custom", "custom": "togglePreview" } } ``` The following example forwards the `manageMenu` native action to a child navbar element: ```json { "actionType": "forward", "forward": { "elementPath": ".self.navbar", "actionName": "manageMenu" } } ``` ### Event action The following example dispatches an `onExportData` event: ```json { "actionType": "event", "event": { "event": "onExportData" } } ``` ### CSS property action The following example opens the styling control for `backgroundColor`: ```json { "actionType": "cssProperty", "cssProperty": { "cssPropertyKey": "backgroundColor" } } ``` ### CSS custom property action The following example opens the styling control for a CSS custom property: ```json { "actionType": "cssCustomProperty", "cssCustomProperty": { "cssCustomPropertyKey": "shadowStyle" } } ``` ### Display group action The following example opens the panel for a display group: ```json { "actionType": "displayGroup", "displayGroup": { "displayGroupKey": "animations" } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Actions Examples ## Article: Actions Examples ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/actions/actions-examples.md ## Article Content: # Actions Examples
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    This article provides practical examples of using native actions, custom actions, and display filters in your component manifests. ## Renaming native actions A simple override that changes the labels without changing the behavior: ```json { "editorElement": { "selector": ".collapsible-text", "displayName": "Collapsible Text", "actions": { "settings": { "displayName": "Text Settings", "execution": { "actionType": "data", "data": { "dataItemKey": "text" } } }, "design": { "displayName": "Text Design", "execution": { "actionType": "cssProperty", "cssProperty": { "cssPropertyKey": "backgroundColor" } } } } } } ``` ## Event action The TemplateContainer component adds a "Settings" custom action that dispatches an event to the editor script: ```json { "editorElement": { "customActions": { "templateContainerSettings": { "displayName": "Settings", "execution": { "actionType": "event", "event": { "event": "templateContainerSettingsAction" } } } } } } ``` ## Forward action The Button component surfaces its child AnimatedIcon's customization panel. The AnimatedIcon is a ref element with `selectable: false`, so users can't select it directly. The forward action lets the parent Button expose the icon's editing UI. When using `actionName: "custom"`, the target element must have a matching `customActions` entry in its own manifest. In this case, the AnimatedIcon component defines `customActions.animatedIcon` which the forward action invokes. ```json { "editorElement": { "selector": ".button", "displayName": "Button", "elements": { "label": { "elementType": "inlineElement", "inlineElement": { "selector": ".label", "displayName": "Label", "cssProperties": { "font": {}, "color": {} } } }, "animatedIcon": { "elementType": "refElement", "refElement": { "displayName": "Icon", "selector": ".animated-icon", "type": "wixEditorElements.AnimatedIcon", "behaviors": { "selectable": false, "removable": true } } } }, "customActions": { "animatedIcon": { "displayName": "Customize Icon", "execution": { "actionType": "forward", "forward": { "elementPath": ".self.animatedIcon", "actionName": "custom", "custom": "animatedIcon" } } } } } } ``` ## Native action overrides with custom actions The Slideshow component overrides `manageItems` to manage slides, and adds a custom forward action for the navigation icon: ```json { "editorElement": { "selector": ".slideshow", "displayName": "Slideshow", "data": { "slides": { "dataType": "arrayItems", "displayName": "Slides", "arrayItems": { "data": { "items": { "name": { "dataType": "text" }, "content": { "dataType": "container", "container": { "selector": ".slide", "containerType": "simple" } } } } } }, "autoplay": { "dataType": "booleanValue", "displayName": "Autoplay", "defaultValue": false } }, "actions": { "manageItems": { "displayName": "Manage Slides", "execution": { "actionType": "data", "data": { "dataItemKey": "slides" } } } }, "customActions": { "animatedIcon": { "displayName": "Navigation Icon", "execution": { "actionType": "forward", "forward": { "elementPath": ".self.navigationButtons.animatedIcon", "actionName": "custom", "custom": "animatedIcon" } } } } } } ``` ## CSS property and display group actions Quick-access buttons for specific styling controls: ```json { "editorElement": { "cssProperties": { "backgroundColor": { "displayName": "Background", "defaultValue": "#ffffff" } }, "cssCustomProperties": { "shadowIntensity": { "cssPropertyType": "number", "displayName": "Shadow Intensity", "defaultValue": "0.1" }, "animationSpeed": { "cssPropertyType": "number", "displayName": "Animation Speed", "defaultValue": "0.3" } }, "displayGroups": { "visualEffects": { "displayName": "Visual Effects", "groupType": "cssDataType", "cssDataType": { "items": ["shadowIntensity", "animationSpeed"] } } }, "customActions": { "editBackground": { "displayName": "Background", "execution": { "actionType": "cssProperty", "cssProperty": { "cssPropertyKey": "backgroundColor" } } }, "visualEffects": { "displayName": "Visual Effects", "execution": { "actionType": "displayGroup", "displayGroup": { "displayGroupKey": "visualEffects" } } } } } } ``` ## Display filters The following example uses `hide` to remove specific actions and data items from the editor: ```json { "editorElement": { "displayFilters": { "data": { "hide": ["svg", "isDecorative"] }, "actions": { "hide": ["media", "design"] } } } } ``` The following example uses `show` to allow only the `settings` and `design` actions to be visible: ```json { "editorElement": { "displayFilters": { "actions": { "show": ["settings", "design"] } } } } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Standard CSS Properties ## Article: Standard CSS Properties ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Standard CSS Properties
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `cssProperties` property of the manifest lets you define standard [CSS properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties) that Wix users can customize through the editor. The editor reads these definitions and generates the appropriate design panel controls for each property. The key must be a valid CSS property name, such as `border`, `backgroundColor`, or `fontSize`. The editor applies the value directly to the element that matches the selector. ## Where to define {/* TODO: when refElements are supported, add here */} You can define `cssProperties` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`. ## cssProperties compared to cssCustomProperties The manifest supports two ways to define styling: `cssProperties` and [`cssCustomProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md). Use `cssProperties` when you want to apply a standard CSS property directly to an element. The editor maps the property to the appropriate design panel control automatically. Consider [`cssCustomProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md) instead when: - You need a variable name that isn't a standard CSS property. - The same value is used across multiple CSS rules. - You want a custom dropdown with specific options. - You need numeric values with min and max constraints. ## CSS property item Each item defined under `cssProperties` accepts the following properties: Default CSS values for defined component.md states.} /> Supported CSS filter functions: Supported CSS display values: Supported CSS writing-mode values: ImageCategoryTypes} description="Which image media manager category to open when the Wix user selects a background." /> VectorArtCategoryTypes} description="Which shape divider media manager category to open when the Wix user selects a background." /> ## CSS shorthand [CSS shorthand properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Shorthand_properties) allow you to set multiple related CSS properties with a single declaration. When you define a shorthand property in the manifest, the editor panel displays separate controls for the core related properties. ## Examples ### Basic CSS property with state defaults The following defines a `border` property with a dashed green default and a solid red override for the error state. ```typescript editorElement: { cssProperties: { border: { displayName: 'Main component Border', defaultValue: 'dashed 2px green', statesDefaultValues: { error: 'solid 2px red', }, }, }, } ``` ### Empty object CSS properties You can define a CSS property with an empty object to enable it with default editor behavior. ```typescript editorElement: { cssProperties: { backgroundColor: {}, border: {}, padding: {}, }, } ``` ### CSS shorthand When you define a shorthand property like `border`, the editor displays separate controls for color, width, and style for all directional variants. ```typescript editorElement: { cssProperties: { border: { displayName: 'Border', defaultValue: '2px solid blue', }, }, } ``` ### Filter and backdrop filter The following defines a `filter` property with blur, drop shadow, and contrast effects, and a `backdropFilter` with blur, brightness, and saturation effects. ```typescript editorElement: { cssProperties: { filter: { displayName: 'Image Effects', filter: { filterFunctions: ['blur', 'drop_shadow', 'contrast'], }, }, backdropFilter: { displayName: 'Backdrop Effects', backdropFilter: { filterFunctions: ['blur', 'brightness', 'saturate'], }, }, }, } ``` ### Display The following defines a `display` property that lets Wix users choose between none, flex, and block layouts. ```typescript editorElement: { cssProperties: { display: { displayName: 'Layout Type', defaultValue: 'flex', display: { displayValues: ['none', 'flex', 'block'], }, }, }, } ``` ### Writing mode The following defines a `writingMode` property that lets Wix users choose between horizontal and vertical text directions. ```typescript editorElement: { cssProperties: { writingMode: { displayName: 'Text Direction', defaultValue: 'horizontalTb', writingMode: { writingModeValues: ['horizontalTb', 'verticalRl', 'verticalLr'], }, }, }, } ``` ### Background The following defines a `background` property that opens the image media manager when a Wix user selects a background. ```typescript editorElement: { cssProperties: { background: { displayName: 'Background', background: { imageCategory: 'IMAGE_BACKGROUND', }, }, }, } ``` ## See also - [CSS Custom Properties](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md) - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: CSS Custom Properties ## Article: CSS Custom Properties ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-custom-properties.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # CSS Custom Properties
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `cssCustomProperties` property of the manifest lets you define [CSS custom properties (CSS variables)](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties) that Wix users can customize through the editor. Unlike [`cssProperties`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md), where keys must be standard CSS property names, `cssCustomProperties` lets you define your own variable names, giving you full control over how style values are applied to your component. ## Where to define {/* TODO: when refElements are supported, add here */} You can define `cssCustomProperties` in [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) and in [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement`. When a Wix user customizes your component, the editor sets these variables as CSS custom properties on the element. Your component's CSS must consume them using [`var()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/var). The connection between the manifest and the rendered styles flows through your CSS, so you decide which CSS properties each variable maps to, and you can use a single variable in multiple places or combine it with other values. ## CSS custom property item `cssCustomProperties` is a map where each key is a variable name without the `--` prefix, such as `customBorder` or `iconColor`, and each value is a CSS custom property item. Every item must include a `cssPropertyType` field, which tells the editor what type of control to display for that variable. Default CSS values for defined component.md states.} /> {/* Currently, only `drop_shadow` has a fully implemented design panel control. The other filter functions are valid schema values but don't yet render UI controls in the editor. You must include `drop_shadow` in your `filterFunctions` array for the filter section to appear in the design panel. */} Which filter effects to show in the editor panel. Only relevant for the filter property.} /> Which backdrop-filter effects to show in the editor panel. Only relevant for the backdropFilter property.} /> Which display options to show in the editor panel. Only relevant for the display property.} /> Which text direction options to show in the editor panel. Only relevant for the writingMode property.} /> Which background options to show in the editor panel. Only relevant for the background property.} /> ## CSS data types Supported CSS data types: - [`angle`](https://developer.mozilla.org/en-US/docs/Web/CSS/angle) - [`blendMode`](https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode) - [`color`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - `customEnum` - [`length`](https://developer.mozilla.org/en-US/docs/Web/CSS/length) - [`lengthPercentage`](https://developer.mozilla.org/en-US/docs/Web/CSS/length-percentage) - [`number`](https://developer.mozilla.org/en-US/docs/Web/CSS/number) - [`percentage`](https://developer.mozilla.org/en-US/docs/Web/CSS/percentage) - [`string`](https://developer.mozilla.org/en-US/docs/Web/CSS/string) - [`time`](https://developer.mozilla.org/en-US/docs/Web/CSS/time) ## CSS property types Supported property types. **CSS data properties:** - [`angle`](https://developer.mozilla.org/en-US/docs/Web/CSS/angle), [`blendMode`](https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode), `customEnum`, [`length`](https://developer.mozilla.org/en-US/docs/Web/CSS/length), [`lengthPercentage`](https://developer.mozilla.org/en-US/docs/Web/CSS/length-percentage), [`number`](https://developer.mozilla.org/en-US/docs/Web/CSS/number), [`percentage`](https://developer.mozilla.org/en-US/docs/Web/CSS/percentage), [`string`](https://developer.mozilla.org/en-US/docs/Web/CSS/string), [`time`](https://developer.mozilla.org/en-US/docs/Web/CSS/time) **Background properties:** - [`background`](https://developer.mozilla.org/en-US/docs/Web/CSS/background), [`backgroundAttachment`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-attachment), [`backgroundClip`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-clip), [`backgroundColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-color), [`backgroundImage`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-image), [`backgroundOrigin`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-origin), [`backgroundPosition`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position), [`backgroundRepeat`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat), [`backgroundSize`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-size) **Margin properties:** - [`margin`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin), [`marginBottom`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom), [`marginInlineEnd`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline-end), [`marginInlineStart`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline-start), [`marginLeft`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left), [`marginRight`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-right), [`marginTop`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-top) **Padding properties:** - [`padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding), [`paddingBottom`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom), [`paddingInlineEnd`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-inline-end), [`paddingInlineStart`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-inline-start), [`paddingLeft`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left), [`paddingRight`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-right), [`paddingTop`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top) **Border properties:** - [`border`](https://developer.mozilla.org/en-US/docs/Web/CSS/border), [`borderBottom`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom), [`borderBottomColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-color), [`borderBottomLeftRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-left-radius), [`borderBottomRightRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-right-radius), [`borderBottomStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-style), [`borderBottomWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-width), [`borderColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-color), [`borderEndEndRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-end-end-radius), [`borderEndStartRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-end-start-radius), [`borderInlineEnd`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-end), [`borderInlineEndColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-end-color), [`borderInlineEndStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-end-style), [`borderInlineEndWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-end-width), [`borderInlineStart`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-start), [`borderInlineStartColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-start-color), [`borderInlineStartStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-start-style), [`borderInlineStartWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-start-width), [`borderLeft`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-left), [`borderLeftColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-left-color), [`borderLeftStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-left-style), [`borderLeftWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-left-width), [`borderRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius), [`borderRight`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-right), [`borderRightColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-color), [`borderRightStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-style), [`borderRightWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-width), [`borderStartEndRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-start-end-radius), [`borderStartStartRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-start-start-radius), [`borderStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style), [`borderTop`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top), [`borderTopColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-color), [`borderTopLeftRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-left-radius), [`borderTopRightRadius`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-right-radius), [`borderTopStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-style), [`borderTopWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-width), [`borderWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/border-width) **Font and text properties:** - [`color`](https://developer.mozilla.org/en-US/docs/Web/CSS/color), [`font`](https://developer.mozilla.org/en-US/docs/Web/CSS/font), [`fontFamily`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family), [`fontSize`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size), [`fontStretch`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch), [`fontStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style), [`fontVariant`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant), [`fontWeight`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight), [`letterSpacing`](https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing), [`lineHeight`](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height), [`textAlign`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align), [`textDecoration`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration), [`textDecorationColor`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-color), [`textDecorationLine`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-line), [`textDecorationStyle`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-style), [`textDecorationThickness`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-thickness), [`textIndent`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-indent), [`textOverflow`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow), [`textShadow`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow), [`textTransform`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform), [`writingMode`](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode) **Layout and display properties:** - [`alignItems`](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items), [`alignSelf`](https://developer.mozilla.org/en-US/docs/Web/CSS/align-self), [`boxShadow`](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow), [`columnGap`](https://developer.mozilla.org/en-US/docs/Web/CSS/column-gap), [`display`](https://developer.mozilla.org/en-US/docs/Web/CSS/display), [`flexDirection`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction), [`gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/gap), [`height`](https://developer.mozilla.org/en-US/docs/Web/CSS/height), [`justifyContent`](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content), [`opacity`](https://developer.mozilla.org/en-US/docs/Web/CSS/opacity), [`overflow`](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow), [`rowGap`](https://developer.mozilla.org/en-US/docs/Web/CSS/row-gap), [`width`](https://developer.mozilla.org/en-US/docs/Web/CSS/width) **Effects properties:** - [`backdropFilter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter), [`filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter), [`isolation`](https://developer.mozilla.org/en-US/docs/Web/CSS/isolation), [`mixBlendMode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode), [`objectFit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit), [`objectPosition`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) **SVG properties:** - [`fill`](https://developer.mozilla.org/en-US/docs/Web/CSS/fill), [`fillOpacity`](https://developer.mozilla.org/en-US/docs/Web/CSS/fill-opacity), [`stroke`](https://developer.mozilla.org/en-US/docs/Web/CSS/stroke), [`strokeOpacity`](https://developer.mozilla.org/en-US/docs/Web/CSS/stroke-opacity), [`strokeWidth`](https://developer.mozilla.org/en-US/docs/Web/CSS/stroke-width) ## Examples ### Basic CSS custom properties The following defines two CSS custom properties: `iconSize` for controlling icon dimensions, and `brandColor` for setting the primary brand color with an error state variant. ```typescript editorElement: { cssCustomProperties: { iconSize: { cssPropertyType: 'length', displayName: 'Icon Size', defaultValue: '24px', }, brandColor: { cssPropertyType: 'color', displayName: 'Brand Color', defaultValue: '#007bff', statesDefaultValues: { error: '#dc3545', }, }, }, } ``` The CSS custom properties defined in the manifest don't apply any styles on their own. Your component's CSS must consume these variables using `var()` to connect them to actual style rules: ```css .icon { width: var(--iconSize, 24px); height: var(--iconSize, 24px); } .button { color: var(--brandColor, #007bff); border-color: var(--brandColor, #007bff); } ``` ### Custom enum The following defines a custom enum that lets Wix users select an icon position relative to the button text using a dropdown in the editor. ```typescript editorElement: { cssCustomProperties: { iconPosition: { cssPropertyType: 'customEnum', displayName: 'Icon Position', defaultValue: 'row', customEnum: { cssPropertyType: 'string', options: [ { value: 'row', displayName: 'Before Text' }, { value: 'row-reverse', displayName: 'After Text' }, { value: 'column', displayName: 'Below Text' }, { value: 'column-reverse', displayName: 'Above Text' }, ], }, }, }, } ``` ### CSS number with constraints The following defines a numeric CSS custom property for controlling animation speed, with constraints that limit the value between 0 and 5 seconds in increments of 0.1. ```typescript editorElement: { cssCustomProperties: { animationDuration: { cssPropertyType: 'number', displayName: 'Animation Speed', defaultValue: '0.3', number: { min: '0', max: '5', multiplier: '0.1', }, }, }, } ``` ## See also - [Standard CSS Properties](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/standard-css-properties.md) - [About the Manifest](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/about-the-manifest.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: How the Editor Resolves CSS Property Values ## Article: CSS Property Value Resolution ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/how-the-editor-resolves-css-property-values.md ## Article Content: import { Property, PropertyList } from "@wix/docs-ui/content"; # How the Editor Resolves CSS Property Values
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    When multiple styling sources exist for the same property, understanding which value is used is essential. This guide explains how CSS property values are resolved. ## CSS property hierarchy The editor resolves which value to apply in this order, from lowest to highest priority: ## CSS properties vs. CSS custom properties The hierarchy above applies to both `cssProperties` and `cssCustomProperties`, but they're applied differently. The editor applies `cssProperties` as inline styles, which automatically override your CSS file. For `cssCustomProperties`, the editor sets CSS variables on the element, but your CSS file must reference them with `var()` for the values to take effect. ## Switching between presets User customizations are tied to the active preset. Switching to a different preset resets any customizations the Wix user made. If they switch back, the preset's original defaults are restored, not the Wix user's previous customizations. ## CSS in states [States](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/states.md) aren't a separate priority level. Both the manifest and presets can define state-specific values using `statesDefaultValues`. The current state determines which value to read from whichever set of defaults is active. ## Display filters Display filters control which properties are visible to the user in the editor, not which values take priority. They follow their own narrowing order: Element filters apply first, then preset filters, then state filters. See [Display Filters](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/display-filters.md) for details. {/* ## See also */} --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: CSS Properties Examples ## Article: CSS Properties Examples ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/css/css-properties-examples.md ## Article Content: # CSS Properties Examples
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    This article provides practical examples of using CSS properties and CSS custom properties in your component manifests. ## Basic CSS property A simple text component with standard CSS properties: ```typescript editorElement: { selector: '.text', displayName: 'Text', cssProperties: { color: { displayName: 'Text Color', defaultValue: '#333333', }, fontSize: { displayName: 'Text Size', defaultValue: '16px', }, fontFamily: { displayName: 'Font', defaultValue: 'sans-serif', }, }, } ``` ## Basic CSS custom property A button component using CSS custom properties (CSS variables): ```typescript editorElement: { selector: '.button', displayName: 'Button', cssCustomProperties: { iconSize: { cssPropertyType: 'length', displayName: 'Icon Size', defaultValue: '24px', }, iconColor: { cssPropertyType: 'color', displayName: 'Icon Color', defaultValue: '#ffffff', }, }, } ``` Your component CSS must reference these variables: ```css .button-icon { width: var(--iconSize, 24px); height: var(--iconSize, 24px); color: var(--iconColor, #ffffff); } ``` ## Nested elements Both CSS properties and CSS custom properties can be defined on nested elements within your component. ```typescript editorElement: { selector: '.card', displayName: 'Card', cssProperties: { backgroundColor: { defaultValue: '#fff' }, boxShadow: {}, }, elements: { title: { elementType: 'inlineElement', inlineElement: { selector: '.card-title', displayName: 'Title', cssProperties: { font: {}, color: { defaultValue: '#333' }, }, }, }, }, } ``` Separate design controls are displayed to users for each element when selected. ## Button component with states A button showing CSS properties with state variations: ```typescript editorElement: { selector: '.button', displayName: 'Button', cssProperties: { backgroundColor: { displayName: 'Background', defaultValue: '#007bff', statesDefaultValues: { disabled: '#cccccc', }, }, color: { displayName: 'Text Color', defaultValue: '#ffffff', statesDefaultValues: { disabled: '#666666', }, }, borderRadius: { displayName: 'Corner Rounding', defaultValue: '4px', }, }, states: { disabled: { displayName: 'Disabled State', }, }, } ``` ## Card component with multiple elements A card component with nested text, image, a drop shadow filter, and a custom property for content spacing: ```typescript editorElement: { selector: '.card', displayName: 'Card', cssProperties: { backgroundColor: { defaultValue: '#ffffff' }, borderRadius: { defaultValue: '8px' }, padding: { defaultValue: '16px' }, filter: { filter: { filterFunctions: ['drop_shadow'], }, }, }, cssCustomProperties: { contentGap: { cssPropertyType: 'gap', displayName: 'Content Spacing', defaultValue: '12px', }, }, elements: { image: { elementType: 'inlineElement', inlineElement: { selector: '.card-image', displayName: 'Image', cssProperties: { objectFit: { defaultValue: 'cover' }, borderRadius: { defaultValue: '4px' }, }, }, }, title: { elementType: 'inlineElement', inlineElement: { selector: '.card-title', displayName: 'Title', cssProperties: { font: {}, color: { defaultValue: '#333333' }, }, }, }, description: { elementType: 'inlineElement', inlineElement: { selector: '.card-description', displayName: 'Description', cssProperties: { font: {}, color: { defaultValue: '#666666' }, }, }, }, }, } ``` Component CSS: ```css .card { display: flex; flex-direction: column; gap: var(--contentGap, 12px); } ``` --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: About Data ## Article: Data Overview ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # About Data
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    The `data` property of your manifest controls how Wix users configure component properties and what data your component receives at [runtime](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md). When building custom components, you often want Wix users to configure specific properties like text, images, links, colors, or dates. Based on your data type definitions, the editor automatically generates the appropriate UI controls in panels in the editor. For example, if you define a date picker type, a date picker control appears when Wix users open the settings panel. The `displayName` you provide becomes the label Wix users see in the editor panel. ## Where to define {/* TODO: when refElements are supported, add here */} You can define `data` in: - [`editorElement`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/editor-element.md) - [`elements`](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/elements.md) of type `inlineElement` The data property is optional. ## Data item fields Each property in your `data` object is a data item. A data item is an object with core fields that every data type shares, plus type-specific fields for additional configuration. ### Core fields Initial value when the component is first added. This is what initially appears in the settings panel for this item. Format must match the runtime.md prop format.} /> ### Type-specific fields Some data types require or allow additional configuration beyond the core fields. When a type supports extra configuration, those options go in a field named after the type itself. For example, a `text` data type can have a `text` field with validation options: ```typescript editorElement: { data: { label: { dataType: "text", displayName: "Label", defaultValue: "Click", text: { maxLength: 100, minLength: 3, }, }, }, } ``` For details on each type and its configuration options, see the type reference pages in [Available data types](#available-data-types). ## Available data types When you define data items in your manifest, you choose from a set of predefined data types. Each type comes with built-in validation, UI controls in the editor, and a [corresponding runtime format](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/runtime-props.md). TypeScript types for all props are available from the [`@wix/editor-react-types`](https://www.npmjs.com/package/@wix/editor-react-types) package. Available data types organized by category: | Category | Data Type | Type-Specific Fields | Description | |----------|-----------|---------------------|-------------| | [Primitive Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/primitive.md) | `text` | ✓ | A simple text value. | | | `number` | ✓ | Numeric values with optional validation constraints. | | | `booleanValue` | ✗ | true or false. | | | `guid` | ✗ | Globally unique identifier. | | [Text Variations](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/text-variations.md) | `textEnum` | ✓ | A list of predefined textual values to choose from. | | | `richText` | ✓ | HTML text with inline CSS styling. | | | `regex` | ✗ | Valid regex pattern. | | | `direction` | ✗ | Text direction. | | [Date and Time](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/date-and-time.md) | `localDate` | ✗ | Local date in ISO-8601 format `YYYY-MM-DD`. | | | `localTime` | ✗ | Local time in ISO-8601 format `hh:mm[:ss][.sss]`. | | | `localDateTime` | ✗ | Local date-time in ISO-8601 format `YYYY-MM-DDThh:mm[:ss][.sss]`. | | [Contact Information](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/contact-information.md) | `email` | ✗ | Standard email address `RFC 5321`. | | | `phone` | ✗ | Phone number with common formatting characters. | | | `webUrl` | ✗ | URL with http or https scheme. | | | `hostname` | ✗ | IANA hostname. | | [Media](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/media.md) | `image` | ✓ | Wix Image object. | | | `video` | ✓ | Wix Video object. | | | `vectorArt` | ✓ | Wix Sanitized Vector Art (SVG). | | | `audio` | ✗ | Wix Audio object. | | [Link](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/link.md) | `link` | ✓ | Wix Link object. | | [Container](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/container.md) | `container` | ✓ | Container for child components. | | [Complex Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/complex-types.md) | `data` | ✓ | Complex nested data structure. | | | `arrayItems` | ✓ | Array of data items. | | | `menuItems` | ✗ | Site navigation menu items. | | [Accessibility](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/accessibility.md) | `a11y` | ✓ | Accessibility fields. | | [Event Handlers](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/event-handler-data.md) | `onClick` | ✗ | Click event handler. | | | `onChange` | ✗ | Input change event handler. | | | `onKeyPress` | ✗ | Key press event handler. | | | `onKeyUp` | ✗ | Key up event handler. | | | `onKeyDown` | ✗ | Key down event handler. | | | `onFocus` | ✗ | Focus event handler. | | | `onSubmit` | ✗ | Form submit event handler. | | | `function` | ✗ | Custom function handler. | --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Primitive ## Article: Primitive Data Types ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/primitive.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Primitive Data Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Primitive data types are the building blocks for component data. They represent simple values like text, numbers, and booleans. ## Text The `text` data type accepts string values with optional validation constraints. These ensure data quality and guide users toward valid input. > **Note:** Specify these additional configurations under a `text` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). > **Note:** If you set a `defaultValue`, it displays when the component is first added. However, if a Wix user deletes the text, the field remains empty, it doesn't revert to the default. ### Example A text field with validation constraints: In the manifest: ```typescript editorElement: { data: { username: { dataType: "text", displayName: "Username", defaultValue: "", text: { minLength: 3, maxLength: 20, pattern: "^[a-zA-Z0-9_]+$", }, }, }, } ``` In the component: ```tsx import type { Text } from '@wix/editor-react-types'; interface UsernameProps { username?: Text; } const Username = ({ username }: UsernameProps) => {username}; ``` ## Guid The `guid` data type represents a globally unique identifier (GUID/UUID). No additional configuration is needed. ### Example A GUID field for unique identification: In the manifest: ```typescript editorElement: { data: { recordId: { dataType: "guid", displayName: "Record ID", defaultValue: "550e8400-e29b-41d4-a716-446655440000", }, }, } ``` In the component: ```tsx import type { Guid } from '@wix/editor-react-types'; interface RecordProps { recordId?: Guid; } const Record = ({ recordId }: RecordProps) => {recordId}; ``` ## Number The `number` data type accepts numeric values with optional validation constraints. Useful for ensuring values stay within acceptable ranges. > **Note:** Specify these additional configurations under a `number` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). ### Example A rating field with min and max constraints: In the manifest: ```typescript editorElement: { data: { rating: { displayName: "Rating", dataType: "number", defaultValue: 3, number: { min: '1', max: '5', }, }, }, } ``` In the component: ```tsx import type { NumberType } from '@wix/editor-react-types'; interface RatingProps { rating?: NumberType; } const Rating = ({ rating }: RatingProps) => {rating} / 5; ``` ## Boolean value The `booleanValue` data type represents true or false values. ### Example A feature toggle field: In the manifest: ```typescript editorElement: { data: { isEnabled: { dataType: "booleanValue", displayName: "Enable Feature", defaultValue: true, }, }, } ``` In the component: ```tsx import type { BooleanValue } from '@wix/editor-react-types'; interface FeatureProps { isEnabled?: BooleanValue; } const Feature = ({ isEnabled }: FeatureProps) => (
    ); ``` ## See also - [Data Overview](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) - [Text Variations](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/text-variations.md) - [Date and Time Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/date-and-time.md) - [Contact Information Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/contact-information.md) - [Media Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/media.md) - [Complex Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/complex-types.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Contact Information ## Article: Contact Information Types ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/contact-information.md ## Article Content: # Contact Information Data Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Contact information data types provide automatic validation for common contact-related data formats. They enforce specific patterns and conventions, ensuring data quality without requiring custom validation logic. ## Email Standard email address according to [RFC 5321, section 4.1.2](https://datatracker.ietf.org/doc/html/rfc5321#section-4.1.2). ### Example A contact email field: In the manifest: ```typescript editorElement: { data: { contactEmail: { dataType: "email", displayName: "Contact Email", defaultValue: "user@example.com", }, }, } ``` In the component: ```tsx import type { Email } from '@wix/editor-react-types'; interface ContactProps { contactEmail?: Email; } const Contact = ({ contactEmail }: ContactProps) => ( {contactEmail} ); ``` ## Phone Phone number format designed to match phone numbers with a variety of common characters, including digits (0-9), spaces, parentheses, plus sign, hyphens, and periods. ### Example A business phone field: In the manifest: ```typescript editorElement: { data: { contactPhone: { dataType: "phone", displayName: "Business Phone", defaultValue: "+1 (555) 123-4567", }, }, } ``` In the component: ```tsx import type { Phone } from '@wix/editor-react-types'; interface BusinessProps { contactPhone?: Phone; } const Business = ({ contactPhone }: BusinessProps) => ( {contactPhone} ); ``` ## Web URL A URL with scheme http or https. ### Example A website URL field: In the manifest: ```typescript editorElement: { data: { websiteUrl: { dataType: "webUrl", displayName: "Website URL", defaultValue: "https://www.example.com", }, }, } ``` In the component: ```tsx import type { WebUrl } from '@wix/editor-react-types'; interface SiteProps { websiteUrl?: WebUrl; } const SiteLink = ({ websiteUrl }: SiteProps) => ( {websiteUrl} ); ``` ## Hostname Hostname according to IANA standards. ### Example An API hostname field: In the manifest: ```typescript editorElement: { data: { apiHost: { dataType: "hostname", displayName: "API Hostname", defaultValue: "api.example.com", }, }, } ``` In the component: ```tsx import type { Hostname } from '@wix/editor-react-types'; interface ApiProps { apiHost?: Hostname; } const ApiConfig = ({ apiHost }: ApiProps) => {apiHost}; ``` ## See also - [Data Overview](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) - [Primitive Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/primitive.md) - [Date and Time Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/date-and-time.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Text Variations ## Article: Text Variations ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/text-variations.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Text Variation Data Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Text variation data types provide specialized text handling beyond simple string values. These types support predefined options, rich formatting, text direction, and pattern validation. ## TextEnum The `textEnum` data type allows users to choose from a predefined set of options rather than entering free text. The editor displays this as a dropdown or radio buttons. > **Note:** Specify these additional configurations under a `textEnum` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). Each option has 2 parts: the internal value and the user-facing display name. ### Example A button size selector with predefined options: In the manifest: ```typescript editorElement: { data: { size: { displayName: "Button Size", dataType: "textEnum", defaultValue: "medium", textEnum: { options: [ { value: 'small', displayName: 'Small' }, { value: 'medium', displayName: 'Medium' }, { value: 'large', displayName: 'Large' }, ], }, }, }, } ``` In the component: ```tsx import type { TextEnum } from '@wix/editor-react-types'; interface ButtonProps { size?: TextEnum; } const Button = ({ size }: ButtonProps) => ( ); ``` ## Rich text Rich text allows users to format content with HTML and inline styles, perfect for descriptions, articles, or any content needing formatting. > **Note:** Specify these additional configurations under a `richText` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). ### Example A product description field with limited formatting options: In the manifest: ```typescript editorElement: { data: { description: { dataType: "richText", displayName: "Product Description", defaultValue: "

    This is bold and italic text with a link.

    ", richText: { abilities: ["font", "color", "textAlign", "bulletedList", "fontWeight"] }, }, }, } ``` In the component: ```tsx import type { RichText } from '@wix/editor-react-types'; interface ProductCardProps { description?: RichText; } const ProductCard = ({ description }: ProductCardProps) => (
    ); ``` ## Regex The `regex` data type validates that the input is a valid regular expression pattern. ### Example A validation pattern field: In the manifest: ```typescript editorElement: { data: { validationPattern: { dataType: "regex", displayName: "Validation Pattern", defaultValue: "^[A-Za-z0-9]+$", }, }, } ``` In the component: ```tsx import type { Regex } from '@wix/editor-react-types'; interface FormFieldProps { validationPattern?: Regex; } const FormField = ({ validationPattern }: FormFieldProps) => ( ); ``` ## Direction Text direction according to the [HTML `dir` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir). There are 3 possible values: - `"ltr"`: Left-to-right - `"rtl"`: Right-to-left - `"auto"`: Automatically detect based on content ### Example A text direction field with auto-detection: In the manifest: ```typescript editorElement: { data: { contentDirection: { dataType: "direction", displayName: "Content Direction", defaultValue: "auto", }, }, } ``` In the component: ```tsx import type { Direction } from '@wix/editor-react-types'; interface TextBlockProps { contentDirection?: Direction; } const TextBlock = ({ contentDirection }: TextBlockProps) => (
    Content
    ); ``` ## See also - [Data Overview](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) - [Primitive Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/primitive.md) - [Date and Time Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/date-and-time.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Container ## Article: Container ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/container.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Container Data Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Containers allow Wix users to add and arrange inner elements within your component. This is essential for building flexible, compositional components like sections, boxes, cards, or any component that should contain other elements. > **Note:** Specify these additional configurations under a `container` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). > **Note:** All containers require `selector` and `containerType`. Then choose 1 container type configuration based on your `containerType` value. ### Example A card with constrained styling and vertical resize: In the manifest: ```typescript editorElement: { data: { cardContent: { dataType: "container", displayName: "Card Content", container: { selector: '.card-content', containerType: "simple", simple: { displayName: "Card Content", layout: { resizeDirection: "vertical", }, style: { border: { disableEditing: true, }, background: { displayName: "Card Background", }, }, behaviors: { selectable: true, }, }, }, }, }, } ``` In the component: ```tsx import type { Container } from '@wix/editor-react-types'; interface CardProps { cardContent?: Container; } const Card = ({ cardContent }: CardProps) => (
    {cardContent}
    ); ``` ## See also - [Data Overview](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md) - [Complex Types](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/complex-types.md) --- > Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Media ## Article: Media Data Types ## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/data-types/media.md ## Article Content: import { Property, PropertyList, ExpandableSection } from "@wix/docs-ui/content"; # Media Data Types
    __Alpha:__ Editor React Components are currently in alpha. This feature is subject to change and may have bugs, issues, and limitations. We're actively improving it based on your feedback.
    Media data types let Wix users select and configure images, videos, and vector graphics in your component. When a Wix user edits a media field, the editor opens the Wix media manager, which is the standard dialog for browsing, uploading, and choosing media. The `category` you set on each media field controls which picker the media manager opens. For example, setting the category to `IMAGE_BACKGROUND` opens the background images picker, while `SHAPE_SOCIAL` opens the social icons picker. If you omit the category, a default picker opens for that media type.
    **Tip:** You can also control which folder the media manager opens to by setting `mediaManagerInitialPath` in your display group options.
    ## Image The `image` data type provides an interface for Wix users to select and configure images. > **Note:** Specify these additional configurations under an `image` field in your [data item](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/editor-react-components/manifest-reference/editor-element/data/about-data.md). ### Example A hero image field: In the manifest: ```typescript editorElement: { data: { heroImage: { dataType: "image", displayName: "Hero Image", defaultValue: { uri: "wix:image://v1/abc123.jpg", url: "https://static.wixstatic.com/media/abc123.jpg", name: "hero-image.jpg", width: 1920, height: 1080, quality: 90, }, image: { category: "IMAGE", }, }, }, } ``` In the component: ```tsx import type { Image } from '@wix/editor-react-types'; interface HeroProps { heroImage?: Image; } const Hero = ({ heroImage }: HeroProps) => ( {heroImage?.alt} ); ``` ## Audio The `audio` data type provides an interface for Wix users to select and configure audio tracks. ### Example An audio track field: In the manifest: ```typescript editorElement: { data: { backgroundMusic: { dataType: "audio", displayName: "Background Music", }, }, } ``` In the component: ```tsx import type { Audio } from '@wix/editor-react-types'; interface PlayerProps { backgroundMusic?: Audio; } const Player = ({ backgroundMusic }: PlayerProps) => (