# CustomFieldsWidget

**Category:** Features/Enrich/Data Extension/Components

## Overview

### Introduction

The `CustomFieldsWidget` component enhances the entity page by displaying user-defined custom fields. It not only shows existing custom fields but also acts as a form, enabling users to fill in data for those fields and submit it. Additionally, the widget provides functionality to manage existing fields and add new ones.


  



### Getting Started

1. Wrap your page with [WidgetsFormProvider](./?path=/story/base-components-providers--widgetsformprovider).

```diff
+ import { WidgetsFormProvider } from '@wix/patterns/page';

export const MyPageProvider = ({ children }) => {

  return (
+   
      
+   
  );
};
```

2. Add `CustomFieldsWidget` to one of your child components and pass the `extendedFields` prop to it from the `entity` object.

```diff
  import { Page } from '@wix/design-system';
  import { MyEntityCard } from '../components/MyEntityCard';
  import { MyEntity } from '@wix/ambassador-v1-entity/types';
+ import { CustomFieldsWidget } from '@wix/patterns';

  function MyPage({ entity }: { entity: MyEntity }) {
    return (
      
        
        
          
            
              
            
+           
+             
+           
          
        
      
    );
  }
```

3. Use the [useWidgetsFormContext](./?path=/story/common-hooks--usewidgetsformcontext) hook to get the `state` object and use it to validate the `CustomFieldsWidget` data.

```diff
  import { MyEntity } from '@wix/ambassador-v1-entity/types';
  import { updateEntity } from '@wix/ambassador-v1-entity/http';
+ import { useWidgetsFormContext } from '@wix/patterns';

  function MySaveButton({ entity }: { entity: MyEntity }) {
    const [isLoading, setLoading] = React.useState(false);
    const httpClient = useHttpClient();
+   const state = useWidgetsFormContext();

    return (
       {
          setLoading(true);
+         const { isValid, data } = await state.validate();
+         if (isValid) {
            httpClient.request(updateEntity({
              entity: {
                ...entity
+               extendedFields: data.extendedFields
              }
            })).then(() => {
              setLoading(false);
            });
+         }
        }}
      >
        {isLoading ?  : 'Save'}
      
    );
  }
```

4. That's it! Now you enabled to fill in data for the custom fields, manage existing, and add new ones.


## Examples

### Basic

This example demonstrates the component with all existing field types including initial values.

```tsx
import React from 'react';
import { CustomFieldsWidget } from '@wix/patterns';
import { Button, Cell, Layout, Loader, Page } from '@wix/design-system';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function Basic() {
  const [isLoading, setLoading] = React.useState(false);

  const demoEntity: DummyEntity = {
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
      },
    },
  };

  return (
    <Page>
      <Page.Header
        title="My Entity Page"
        actionsBar={
          <Button
            disabled={isLoading}
            onClick={async () => {
              setLoading(true);
              window.setTimeout(() => {
                setLoading(false);
              }, 1000);
            }}
          >
            {isLoading ? <Loader size="tiny" /> : 'Save'}
          </Button>
        }
      />
      <Page.Content>
        <Layout>
          <Cell>
            <CustomFieldsWidget
              fqdn="wix.patterns.dummyservice.v1.dummy_entity"
              extendedFields={demoEntity.extendedFields}
            />
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

---

### Responsive

This example demonstrates the component in a responsive layout.

```tsx
/* eslint-disable import/no-extraneous-dependencies */

import React from 'react';
import { CustomFieldsWidget } from '@wix/patterns';
import { Card, Cell, Layout, Page } from '@wix/design-system';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function Responsive() {
  const demoEntity: DummyEntity = {
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
      },
    },
  };

  return (
    <Page>
      <Page.Header title="Entity Page" />
      <Page.Content>
        <Layout>
          <Cell span={8}>
            <CustomFieldsWidget
              fqdn="wix.patterns.dummyservice.v1.dummy_entity"
              extendedFields={demoEntity.extendedFields}
            />
          </Cell>
          <Cell span={4}>
            <Card>
              <Card.Header title="Content" />
              <Card.Content />
            </Card>
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

---

### Empty State

This example demonstrates the component in an empty state.

```tsx
/* eslint-disable import/no-extraneous-dependencies */

import React from 'react';
import { CustomFieldsWidget } from '@wix/patterns';
import { Card, Cell, Layout, Page } from '@wix/design-system';

function EmptyState() {
  return (
    <Page>
      <Page.Header title="Entity Page" />
      <Page.Content>
        <Layout>
          <Cell>
            <CustomFieldsWidget fqdn="wix.patterns.dummyservice.v1.dummy_entity" />
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

---

### Inline Theme

This example shows the component in an inline layout - without card.

```tsx
import React from 'react';
import { CustomFieldsWidget } from '@wix/patterns';
import { Button, Cell, Layout, Loader, Page } from '@wix/design-system';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function InlineTheme() {
  const [isLoading, setLoading] = React.useState(false);

  const demoEntity: DummyEntity = {
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
      },
    },
  };

  return (
    <Page>
      <Page.Header
        title="My Entity Page"
        actionsBar={
          <Button
            disabled={isLoading}
            onClick={async () => {
              setLoading(true);
              window.setTimeout(() => {
                setLoading(false);
              }, 1000);
            }}
          >
            {isLoading ? <Loader size="tiny" /> : 'Save'}
          </Button>
        }
      />
      <Page.Content>
        <Layout>
          <Cell>
            <CustomFieldsWidget
              fqdn="wix.patterns.dummyservice.v1.dummy_entity"
              extendedFields={demoEntity.extendedFields}
              theme="inline"
            />
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

---

### Controllable state

This example shows how to control the state, and use custom loading and error UIs

```tsx
import React from 'react';
import {
  CustomFieldsWidget,
  useCustomFieldsWidget,
  useSelector,
} from '@wix/patterns';
import { Button, Cell, Layout, Loader, Page } from '@wix/design-system';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function ControllableState() {
  const [isLoading, setLoading] = React.useState(false);

  const demoEntity: DummyEntity = {
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
      },
    },
  };

  function MyCustomFields() {
    const state = useCustomFieldsWidget({
      fqdn: 'wix.patterns.dummyservice.v1.dummy_entity',
    });
    const isLoading = useSelector(() => state.isLoading);
    const isError = useSelector(() => state.isError);

    if (isLoading) {
      return <div>Loading...</div>;
    }

    if (isError) {
      return <div>Error</div>;
    }

    return (
      <CustomFieldsWidget
        extendedFields={demoEntity.extendedFields}
        theme="inline"
        state={state}
      />
    );
  }

  return (
    <Page>
      <Page.Header
        title="My Entity Page"
        actionsBar={
          <Button
            disabled={isLoading}
            onClick={async () => {
              setLoading(true);
              window.setTimeout(() => {
                setLoading(false);
              }, 1000);
            }}
          >
            {isLoading ? <Loader size="tiny" /> : 'Save'}
          </Button>
        }
      />
      <Page.Content>
        <Layout>
          <Cell>
            <MyCustomFields />
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

---

### Without entity page

This example demonstrates how to use the component without an entity page. Use the hook useCustomFields with your fqdn and get the change in `onChange` callback.

```tsx
import React from 'react';
import {
  CustomFieldsWidget,
  useCustomFieldsWidget,
  useSelector,
} from '@wix/patterns';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function WithoutEntityPage() {
  const demoEntity: DummyEntity = {
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
      },
    },
  };

  const state = useCustomFieldsWidget({
    fqdn: 'wix.patterns.dummyservice.v1.dummy_entity',
  });

  return (
    <>
      {demoEntity.extendedFields && (
        <CustomFieldsWidget
          extendedFields={demoEntity.extendedFields}
          theme="inline"
          state={state}
          onChange={() => {
            console.log('onChange');
          }}
        />
      )}
    </>
  );
}
```

---

### With TPA Fields

In case the field was created with read and write permissions for users, it will be rendered here

```tsx
import React from 'react';
import { CustomFieldsWidget } from '@wix/patterns';
import { Button, Cell, Layout, Loader, Page } from '@wix/design-system';
import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';

function WithTpaFields() {
  const [isLoading, setLoading] = React.useState(false);

  const [demoEntity] = React.useState<DummyEntity>(() => ({
    id: 'id',
    name: 'Hello world!',
    extendedFields: {
      namespaces: {
        _user_fields: {
          shortText: 'Hello world!',
          decimal: 3.14,
          integer: 43,
        },
        '@company/app': {
          app_longText: 'Hello world!',
          app_integer: 43,
        },
      },
    },
  }));

  return (
    <Page>
      <Page.Header
        title="My Entity Page"
        actionsBar={
          <Button
            disabled={isLoading}
            onClick={async () => {
              setLoading(true);
              window.setTimeout(() => {
                setLoading(false);
              }, 1000);
            }}
          >
            {isLoading ? <Loader size="tiny" /> : 'Save'}
          </Button>
        }
      />
      <Page.Content>
        <Layout>
          <Cell>
            <CustomFieldsWidget
              fqdn="wix.patterns.dummyservice.v1.dummy_entity"
              extendedFields={demoEntity.extendedFields}
            />
          </Cell>
        </Layout>
      </Page.Content>
    </Page>
  );
}
```

## API

### Manually wrapping with WidgetsFormProvider

To gain more control over the app structure, the app needs to be wrapped manually with the [WidgetsFormProvider](./?path=/story/base-components-providers--widgetsformprovider) component. This is useful when you want to use the [useWidgetsFormContext](./?path=/story/common-hooks--usewidgetsformcontext) hook in a component that is not a direct child of the `Page` component.

> Note: `useWidgetsFormContext` must still be used inside a direct child of the `WixPatternsBMProvider` component.

```diff
  import React from 'react';
  import {
    CustomFieldsWidget,
+   useWidgetsFormContext
  } from '@wix/patterns';
+ import { WidgetsFormProvider } from '@wix/patterns/page';
  import { WixPatternsBMProvider } from '@wix/patterns/bm';
  import { Button, Cell, Layout, Page } from '@wix/design-system';
  import { DummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/types';
  import { updateDummyEntity } from '@wix/ambassador-cairo-dummyservice-v1-dummy-entity/http';
  import { useHttpClient } from '@wix/yoshi-flow-bm';

// app.tsx
export const MyApp = () => {
  return (
    
      
        
      
    
  );
};

// components/my-entity-page-provider.tsx
export const MyPageProvider = ({ children }) => {

  return (
    
+     
        {children}
+     
    
  );
};

// pages/my-entity-page.tsx
export const MyEntityPage = ({ dummyEntity }: { dummyEntity: DummyEntity }) => {
  const httpClient = useHttpClient();

+ const state = useWidgetsFormContext();

  return (
    
       {
              const { isValid, data } = await state.validate();

              if (isValid) {
                setLoading(true);
                httpClient
                  .request(
                    updateDemoEntity({
                      dummyEntity: {
                        id: dummyEntity.id,
                        name: dummyEntity.name,
  +                     extendedFields: data.extendedFields,
                      },
                    }),
                  )
                  .finally(() => setLoading(false));
              }
            }}
          >
            Save
          
        }
      >
    />
      
+       
      
    
  );
};
```


### Props

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `subtitle` | `string` | No | - | Custom fields widget subtitle. |
| `dataHook` | `string` | No | - | Applies a data-hook HTML attribute that can be used in tests |
| `extendedFields` | `ExtendedFields` | No | - | Existing extended fields values, usually under `myEntity.extendedFields`. |
| `fieldName` | `string` | No | - | The fieldName in the object passed to the `onSave` callback. |
| `emptyStateOverrides` | `{ title?: string; subtitle?: string; } \| undefined` | No | - | Empty state content overrides |
| `theme` | `"inline" \| "card"` | No | - | The theme of the widget - "card" (default) or "inline" |
| `onChange` | `OnCustomFieldChange` | No | - | A callback that is invoked when a custom field changes. @param id The id of the custom field @param value The value of the custom field @param allFields The current values of all the fields @param namespace The namespace of the custom field |
| `fqdn` | `string` | No | - | The FQDN to be used to extract schema from data-extension service. |
| `state` | `CustomFieldsWidgetState` | No | - | Pass CustomFieldsWidgetState from useCustomFieldsWidget hook if you want to control the loading and error state from outside |

## BI

### Custom Fields Events

| Event | Description |
| ----- | ----------- |
| [144:110](https://bo.wix.com/data-tools/bi-catalog-app/event/144:110) | Sent when a Cairo component starts loading. |
| [144:111](https://bo.wix.com/data-tools/bi-catalog-app/event/144:111) | Sent when a Cairo component is done loading. |
| [144:153](https://bo.wix.com/data-tools/bi-catalog-app/event/144:153) | Send when a user click on a section CTA. |
| [144:119](https://bo.wix.com/data-tools/bi-catalog-app/event/144:119) | Sent when a feature sidebar/menu in a Cairo component is opened or closed. |
| [144:141](https://bo.wix.com/data-tools/bi-catalog-app/event/144:141) | Sent when a user clicks on a "try again" CTA. |
| [144:155](https://bo.wix.com/data-tools/bi-catalog-app/event/144:155) | Sent when a user click on a "Add Custom Field". |
| [144:156](https://bo.wix.com/data-tools/bi-catalog-app/event/144:156) | Sent when a user click on cancel / add field in the "Add Custom Field" modal. |
| [144:164](https://bo.wix.com/data-tools/bi-catalog-app/event/144:164) | Sent when a an error in Cairo component loading process happen.  |



### Component load events
Event   |   Description   |
--------- | --------------- |
[144:110](https://bo.wix.com/data-tools/bi-catalog-app/event/144:110) | Sent when a Wix Patterns component starts loading
[144:111](https://bo.wix.com/data-tools/bi-catalog-app/event/144:111) | Sent when a Wix Patterns component is done loading


#### 🐪 Couldn't find what you need?
* Check our [BI Catalog](https://bo.wix.com/data-tools/bi-catalog-app?viewId=all-items-view&selectedColumns=src%2Cevid%2Cname%2Cowner%2Cproduct%2CuserType+false%2CdateUpdated%2CdateCreated+false%2CcreatedBy+false%2Cstatus&source=%5B%7B%22id%22%3A%22144%22%2C%22name%22%3A%22144+-+Cairo%22%7D%5D)
* Contact us on [#cairo-bi](https://wix.slack.com/archives/C03N53KURH9)


