---
title: Widget Tutorial
description: Follow along with this guide to learn how to create a custom widget for Homepage. We'll cover the basic structure of a widget, how to use translations, and how to fetch data from an API.
---

In this guide, we'll walk through the process of creating a custom widget for Homepage. We'll cover the basic structure of a widget, how to use translations, and how to fetch data from an API. By the end of this guide, you'll have a solid understanding of how to build your own custom widget.

**Prerequisites:**

- Basic knowledge of React and JavaScript
- Familiarity with the Homepage platform
- Understanding of JSON and API interactions

Throughout this guide, we'll use `yourwidget` as a placeholder for the unique name of your custom widget. Replace `yourwidget` with the actual name of your widget. It should contain only lowercase letters and no spaces.

This guide makes use of a fake API, which would return a JSON response as such, when called with the `v1/info` endpoint:

```json
{ "key1": 123, "key2": 456, "key3": 789 }
```

## Set up the widget definition

Create a new folder for your widget in the `src/widgets` directory. Name the folder `yourwidget`.

Inside the `yourwidget` folder, create a new file named `widget.js`. This file will contain the metadata for your widget.

Open the `widget.js` file and add the following code:

```js title="src/widgets/yourwidget/widget.js"
import genericProxyHandler from "utils/proxy/handlers/generic"; // (1)!

const widget = /* (2)! */ {
  api: "{url}/{endpoint}" /* (3)! */,
  proxyHandler: genericProxyHandler /* (1)! */,

  mappings: /* (4)! */ {
    info: /* (5)! */ {
      endpoint: "v1/info" /* (6)! */,
    },
  },
};

export default widget;
```

1. We import the `genericProxyHandler` from the `utils/proxy/handlers/generic` module. The `genericProxyHandler` is a generic handler that can be used to fetch data from an API. We then assign the `genericProxyHandler` to the `proxyHandler` property of the `widget` object. There are other handlers available that you can use depending on your requirements. You can also create custom handlers if needed.
2. We define a `widget` object that contains the metadata for the widget.
3. The API endpoint to fetch data from. You can use placeholders like `{url}` and `{endpoint}` to dynamically generate the API endpoint based on the widget configuration.
4. An object that contains mappings for different endpoints. Each mapping should have an `endpoint` property that specifies the endpoint to fetch data from.
5. A mapping named `info` that specifies the `v1/info` endpoint to fetch data from. This would be called from the component as such: `#!js useWidgetAPI(widget, "info");`
6. The `endpoint` property of the `info` mapping specifies the endpoint to fetch data from. There are other properties you can pass to the mapping, such as `method`, `headers`, and `body`.

!!! warning "Important"

    All widgets that fetch data from dynamic endpoints should have either `mappings` or an `allowedEndpoints` property.

## Including translation strings in your widget

Refer to the [translations guide](translations.md) for more details. The Homepage community prides itself on being multilingual, and we strongly encourage you to add translations for your widgets.

## Create the widget component

Create a new file for your widgets component, named `component.jsx`, in the `src/widgets/yourwidget` directory. We'll build the contents of the `component.jsx` file step by step.

First, we'll import the necessary dependencies:

```js title="src/widgets/yourwidget/component.jsx" linenums="1"
import { useTranslation } from "next-i18next"; // (1)!

import Container from "components/services/widget/container"; // (2)!
import Block from "components/services/widget/block"; // (3)!
import useWidgetAPI from "utils/proxy/use-widget-api"; // (4)!
```

1. `#!js useTranslation()` is a hook provided by `next-i18next` that allows us to access the translation strings
2. `#!jsx <Container>` and `#!jsx <Block>` are custom components that we'll use to structure our widget.
3. `#!jsx <Container>` and `#!jsx <Block>` are custom components that we'll use to structure our widget.
4. `#!js useWidgetAPI(widget, endpoint)` is a custom hook that we'll use to fetch data from an API.

---

Next, we'll define a functional component named `Component` that takes a `service` prop.

```js title="src/widgets/yourwidget/component.jsx" linenums="7"
export default function Component({ service }) {}
```

---

We grab the helper functions from the `useTranslation` hook.

```js title="src/widgets/yourwidget/component.jsx" linenums="8"
const { t } = useTranslation();
```

---

We destructure the `widget` object from the `service` prop. The `widget` object contains the metadata for the widget, such as the API endpoint to fetch data from.

```js title="src/widgets/yourwidget/component.jsx" linenums="9"
const { widget } = service;
```

---

Now, the fun part! We use the `useWidgetAPI` hook to fetch data from an API. The `useWidgetAPI` hook takes two arguments: the `widget` object and the API endpoint to fetch data from. The `useWidgetAPI` hook returns an object with `data` and `error` properties.

```js title="src/widgets/yourwidget/component.jsx" linenums="10"
const { data, error } = useWidgetAPI(widget, "info");
```

!!! tip "API Tips"

    You'll see here how part of the API url is built using the `url` and `endpoint` properties from the widget definition.

    In this case, we're fetching data from the `info` endpoint.  The `info` endpoint is defined in the `mappings` object.  So the full API endpoint will be `"{url}/v1/info"`.

    The mapping and endpoint are often the same, but must be defined regardless.

---

Next, we check if there's an error or no data.

If there's an error, we return a `Container` and pass it the `service` and `error` as props. The `Container` component will handle displaying the error message.

```js title="src/widgets/yourwidget/component.jsx" linenums="12"
if (error) {
  return <Container service={service} error={error} />;
}
```

---

If there's no data, we return a `Container` component with three `Block` components, each with a `label`.

```js title="src/widgets/yourwidget/component.jsx" linenums="16"
if (!data) {
  return (
    <Container service={service}>
      <Block label="yourwidget.key1" />
      <Block label="yourwidget.key2" />
      <Block label="yourwidget.key3" />
    </Container>
  );
}
```

This will render the widget with placeholders for the data, i.e., a skeleton view.

!!! tip "Translation Tips"

      The `label` prop in the `Block` component corresponds to the translation key we defined earlier in the `common.json` file.  All text and numerical content should be translated.

---

If there is data, we return a `Container` component with three `Block` components, each with a `label` and a `value`.

Here we use the `t` function from the `useTranslation` hook to translate the data values. The `t` function takes the translation key and an object with variables to interpolate into the translation string.

We're using the `common.number` translation key to format the data values as numbers. This allows for easy localization of numbers, such as using commas or periods as decimal separators.

There are a large number of `common` numerical translation keys available, which you can learn more about in the [Translation Guide](translations.md).

```js title="src/widgets/yourwidget/component.jsx" linenums="26"
return (
  <Container service={service}>
    <Block label="yourwidget.key1" value={t("common.number", { value: data.key1 })} />
    <Block label="yourwidget.key2" value={t("common.number", { value: data.key2 })} />
    <Block label="yourwidget.key3" value={t("common.number", { value: data.key3 })} />
  </Container>
);
```

---

Here's the complete `component.jsx` file:

```js title="src/widgets/yourwidget/component.jsx" linenums="1"
import { useTranslation } from "next-i18next";

import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";

export default function Component({ service }) {
  const { t } = useTranslation();
  const { widget } = service;
  const { data, error } = useWidgetAPI(widget, "info");

  if (error) {
    return <Container service={service} error={error} />;
  }

  if (!data) {
    return (
      <Container service={service}>
        <Block label="yourwidget.key1" />
        <Block label="yourwidget.key2" />
        <Block label="yourwidget.key3" />
      </Container>
    );
  }

  return (
    <Container service={service}>
      <Block label="yourwidget.key1" value={t("common.number", { value: data.key1 })} />
      <Block label="yourwidget.key2" value={t("common.number", { value: data.key2 })} />
      <Block label="yourwidget.key3" value={t("common.number", { value: data.key3 })} />
    </Container>
  );
}
```

## Add the widget to the Homepage

To add your widget to the Homepage, you need to register it in the `src/widgets/widgets.js` file.

Open the `src/widgets/widgets.js` file and import the `Component` from your widget's `component.jsx` file. Please keep the alphabetical order.

```js
// ...
import yourwidget from "./yourwidget/widget";
// ...
```

Add `yourwidget` to the `widgets` object. Please keep the alphabetical order.

```js
const widgets = {
  // ...
  yourwidget: yourwidget,
  // ...
};
```

You also need to add the widget to the `components` object in the `src/widgets/components.js` file.

Open the `src/widgets/components.js` file and import the `Component` from your widget's `component.jsx` file.

Please keep the alphabetical order.

```js
const components = {
  // ...
  yourwidget: dynamic(() => import("./yourwidget/component")),
  // ...
};
```

## Using the widget

You can now use your custom widget in your Homepage. Open your `services.yaml` file and add a new service with the `yourwidget` widget.

```yaml
- Services:
    - Your Widget:
        icon: yourwidget.svg
        href: https://example.com/
        widget:
          type: yourwidget
          url: http://127.0.0.1:1337
```

!!! tip "API Tips"

    You'll see here how part of the API url is built using the `url` and `endpoint` properties from the widget definition.

    We defined the api endpoint as `"{url}/{endpoint}"`.  This is where the `url` is defined.  So the full API endpoint will be `http://127.0.0.1:1337/{endpoint}`.

---

That's it! You've successfully created a custom widget for Homepage. If you have any questions or need help, feel free to reach out to the Homepage community for assistance. Happy coding!
