Tutorial | Create a Custom Products Catalog App with the CLI

One of the ways that 3rd-party developers can extend the Wix ecosystem is by creating apps that appear in site owners' dashboards. These apps can work with data from the sites they’re installed on, such as Content Management System (CMS) data and business solution data like CRM.

This tutorial explains how to create a React app similar to our Custom Products Catalog template where users can add or remove products from a Wix Stores products catalog through a dashboard page.

We use the following technologies to create the app:

The end result will look like this:

We use the following steps to build the “Custom Products Catalog” app:

  1. Initialize the app
  2. Set up the dashboard page
  3. Add permissions
  4. Set up Wix Stores
  5. Install dependencies
  6. Set up wrappers for our dashboard page component
  7. Create hooks for our SDK calls
  8. Create a "Create Product" modal
  9. Create a dashboard page component
  10. Test the app
  11. Build and deploy the app

Before you begin

Before getting started, make sure that:

We also recommend that you check out the Patterns documentation. 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 Product Catalog" app. In the process of initializing our app, the Wix CLI automatically:

  • Creates a new app in the Custom Apps page 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.
  • Prompts you to set up a development site for testing 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:

    Copy
  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 Custom Products Catalog.

  6. Back in the terminal, press Enter to select the default folder name (custom-products-catalog) for your app in your local file system.

You now have a new app in the Custom Apps page, a new folder in your local file system, and a local Git repository for developing and testing your app.

Step 2 | Set up the dashboard page

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. The local development environment runs the app and its initial boilerplate code on the development site that you chose in the previous step.

To run a local development server for your app:

  1. Navigate to your newly-created folder for your app.

    Copy
  2. Run the following command:

    Copy
  3. 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 something like Dev Site 5x2043, and can be found in your Wix account’s list of sites.

  4. Follow the prompt to open the development site on your default browser. If the browser doesn’t open, install your app on your test site manually and skip the next step.

  5. Click Agree & Add to install your app on your development site.

  6. Press 1 to open the local environment in your browser. Your development site’s dashboard opens, and you can see your newly-created app’s dashboard page in the left sidebar. We add the content of our app’s dashboard page in the next step.

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 3 | 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 queryProducts(), createProduct(), and deleteProduct() functions in the Wix SDK’s Stores Products API 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.

You can find the permission scopes you need for each API in the individual function’s page in the JavaScript SDK documentation.

Let’s first look for the permissions that we need in the Permission Scopes section of the queryProducts() function’s page.

The Permission Scopes section indicates that our app must have one of the permission scopes on the list to call the function. Let’s choose the Manage Stores - All Permissions permission scope. It works with createProduct() and deleteProduct() as well.

To add permission requirements for the app:

  1. Go to Permissions tab in your app's dashboard.
  2. Click Add Permissions.
  3. Enter Manage Stores - All Permissions in the Search by name or ID field.
  4. Check the permission scope's checkbox under Choose Permission Scopes.
  5. Click Save.

Step 4 | 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 request the required permissions:

  1. Install the Wix Stores app on your test site. You can create some new products, or use the sample ones provided by Wix.

    Note: Make sure to add Wix Stores in both the Dashboard and the Editor.

  2. In your test site dashboard, select Apps > Manage Apps from the sidebar.
  3. Delete your app from the site.
  4. Open your app’s dashboard in the Custom Apps page.
  5. Select Test Your App > Dashboard. Select your test site in the Site Selector. Follow the prompts to reinstall the app and approve the new permissions.

    Note: You'll be prompted to create an extension before testing the app. Ignore this prompt and click Test Your App.

Step 5 | Install dependencies

Before we start coding our app, we need to install some npm packages. In your terminal, run the following commands:

Copy

The purpose of each of these packages will become clear as we progress.

Step 6 | 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 > dashboard named withProviders.tsx.

  2. Import the react library, and then WixDesignSystemProvider:

    Copy

    WixDesignSystemProvider provides styling for Wix Design System components.

  3. Import WixPatternsProvider:

    Copy

    WixPatternsProvider provides styling and data manipulation for Wix Patterns components.

  4. Import withDashboard:

    Copy

    withDashboard() is a higher-order component that is temporarily required for use in Wix Patterns. It will continue to be supported while it is required. We'll wrap our dashboard page with this component.

  5. Write a function named withProviders to wrap our dashboard page component in WixPatternsProvider, WixDesignSystemProvider, and withDashboard().

    Copy

Your complete withProviders.tsx file should look like this:

Copy

Step 7 | 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 > dashboard named hooks.

  2. Create a new file in src > dashboard > hooks named stores.ts.

  3. Import products from Wix Stores so we can use the Wix Stores Products API:

    Copy
  4. Import useCallback:

    Copy

    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 products. Whenever any of these dependencies change, useCallback redefines the callback function.

  5. Import CollectionOptimisticActions:

    Copy

    CollectionOptimisticActions is a class that provides a set of utilities for managing optimistic actions on a Wix 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.

  6. Create a function to create new products named useCreateProduct:

    Copy

    Inside this function:

    1. Use products to get the createProduct() function.

      Copy
    2. Use useCallback() to manage our createProduct() call:

      Copy

      Let's break down the above code:

      • Lines 2-13: Define the properties of the new product.
      • Lines 15-26: Use optimisticActions.createOne() to create the new product.
        • Lines 16-20: Define the submit function to handle product creation.
        • 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 27: Define the dependencies for useCallback().

    Your function should look like this:

    Copy
  7. Create a function to delete products named useDeleteProducts():

    Copy

    Inside this function:

    1. Use products to get the deleteProduct() function.

      Copy
    2. Use useCallback() to manage our deleteProduct() calls:

      Copy

      Let's break down the above code:

      • Lines 2-18: 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.
        • Lines 9-14: Define the toast to show when a product is created successfully.
        • Lines 15-17: Define the toast to show when a product is not created successfully.
      • Line 19: Define the dependencies for useCallback().

    Your function should look like this:

    Copy

Your complete stores.ts file should look like this:

Copy

Step 8 | 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 > dashboard named components.

  2. Create a new file in src > dashboard > components named create-product.tsx.

  3. Import:

    • React, useEffect, and useState.
    • The components of the Wix Design System required to create our modal. Learn more about the Modal component.
    Copy
  4. Create a CreateProductModal component to define the appearance and functionality of the modal. Use the following code:

    Copy

    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:

Copy

Step 9 | Create a dashboard page component

Finally, we have all the pieces in place to set up our dashboard page.

But first, let's rename our page to something more appropriate:

  1. In your app's repo, navigate to src > dashboard > pages.
  2. In the file page.json, change the value of "title" to "Custom Products Catalog".

Now, let's create the dashboard page itself:

  1. Open the page.tsx file.

  2. Delete all the contents - we're starting from scratch.

  3. Add the following import statements:

    Copy

    This file is where we start to use Patterns.

    Patterns is a React library with advanced components that extend the functionality of the core UI React components from the 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.

  4. Import everything we set up in the previous sections:

    Copy
  5. Define types for filtering the table's data:

    Copy

    These are the product properties by which the table can be filtered. We'll pass this to the useTableCollection hook later.

  6. Define supported fields for sorting the table's data:

    Copy

    This line creates a type which we use later to sort fields in ascending or descending order.

    For a full understanding, let's break this line down:

    • Parameters<products.ProductsQueryBuilder['ascending']>: This part accesses the type definition of the ascending method of the products.ProductsQueryBuilder object. The Parameters type extracts the parameter types of a function or method. In this case, it retrieves the parameter types of the ascending method.
    • [0]: This part accesses the first parameter type of the ascending method. Since the ascending method is expected to take only one parameter (the field name to sort by), accessing the first element effectively gives us the field type that can be used for sorting in ascending order.
    • Parameters<products.ProductsQueryBuilder['descending']>[0]: This does the same thing for the descending method of the products.ProductsQueryBuilder object.
    • SupportedQueryFields: Combining the types extracted from the ascending and descending methods using the OR operator |, the SupportedQueryFields type represents the set of field types that can be used for sorting in the products query builder. This ensures that only valid field types can be used for sorting operations.
  7. Map product types to strings so that they display in a readable way in the table:

    Copy

Create the Products() component

It's finally time to make our dashboard page component. We will:

Define and export our dashboard page component

  1. Create a function for our dashboard page component named Products:

    Copy
  2. Export the component wrapped by withProviders():

    Copy

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.

    Copy
  2. Use products to get the queryProducts() and deleteProduct() functions.

    Copy
  3. Create the tableState using 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 query that retrieves the table data, depending on what filters, sorting, and search is applied in the table.
    • The error message to display if no data is found.
    • How the filters are applied.
    Copy

    Let’s break down the above code:

    • Lines 2-5: Define basic data necessary for the query used to populate the table.
    • Lines 7-50: Define the function that fetches the collection data used to populate the table.
      • Line 8: Deconstruct the query parameter. query is an object that is passed to the fetchData function. It contains all the information needed to construct the query builder.
      • Line 9: Use the queryProducts() function that we retrieved from the products module to create the query builder using limit and offset values.
      • Lines 11-13: Add a search to the query builder if search is included in the query.
      • Lines 15-31: Add the relevant filters to the query builder if filters is included in the query. Use the properties of filters to check if a filter has been applied for Type or Last Updated, which we defined earlier in the TableFilters type.
      • Lines 33-42: Add the relevant sort to the query builder if sort is included in the query.
      • Lines 44-49: Run the query and return the results.
    • Line 52: Define the error message that will be shown if the fetch fails.
    • Lines 54-57: Define which filter factories are used with the filters.
  4. Create the optimisticActions class using 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.

    Copy

    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-31: 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-9: Return false if the product wouldn't pass the search filter.
      • Lines 11-13: Return false if the product wouldn't pass the productType filter.
      • Lines 15-27: Return false if the product wouldn't pass the lastUpdated filter.
      • Line 29: Return true if the product would pass all of the filters.
  5. Define the createProduct() and deleteProducts() functions using the hooks we created in Step 7:

    Copy

At this stage, your Products() function should look like this:

Copy

Create the page structure

We define our page as follows:

  1. Define the CollectionPage component that will wrap all our components on this page:

    Copy
  2. Add a CollectionPage.Header component that contains the page title, breadcrumbs, and a button that opens our Add Product modal:

    Copy
  3. Add a CollectionPage.Content component below the CollectionPage.Header. This will wrap the rest of the components on this page.

    Copy
  4. Add the CreateProductModal component that we created in Step 8:

    Copy
  5. Add a Table component.

    The following code displays a table containing our columns and fetched data. When no items are selected, there is also a search bar, 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.

    Copy

    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.

    • Lines 4-16: Define the table's filters. We use a CollectionToolbarFilters component to display an accordion with the filtering options.
      • Lines 6-10: Define the Type filter. The RadioCollectionFilter defines the way the filter options are displayed in the accordion.
      • Lines 11-14: Define the Last Updated filter. The DateRangeFilter defines the way the filter options are displayed in the accordion.
    • Lines 22-38: Use the MultiBulkActionToolbar component to create the 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 30). If the button is disabled, a message is displayed explaining why (lines 26-28).
    • Lines 43-99: Define the table's columns.
      • Lines 44-52: Product avatar - Use the Image component from the Wix Design System and feed it the image URL from the product object's nested media.mainMedia.image.url field.
      • Lines 53-69: 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 description.
      • Lines 70-76: Product price - Add the product's price prefixed with a dollar sign.
      • Lines 77-88: Product type - Add the product type, formatted using productTypeToDisplayName which we defined earlier. If there is no product type, return an empty string.
      • Lines 89-98: Product last updated date - Add the product's last updated date, formatted as a readable string.
    • Lines 103-116: Create a Delete button for each product (line 108). Add success (lines 111-114) and error (line 115) toasts.

Your page structure code should look like this:

Copy

Complete page code

Your complete code should look like this:

Copy

Step 10 | 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 npm run dev command in your terminal. Your app’s dashboard page will now look like this:

  2. Click the Add Product button and use the modal to add a product.

  3. Select some products and click the Delete button.

  4. If things don’t look right, open your browser’s developer tools and check for errors in the console.

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

Did this help?