> Portal Navigation:
>
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - Top-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.

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-hosted 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-hosting for Wix 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).