# SettingsPage

**Category:** Base Components/Pages/Settings Page

## Overview

### Introduction

The `SettingsPage` component serves as a container that encapsulates the logic and layout structure for rendering an settings page. It provides a structured and organized way to display and manage information related to a specific settings.

Here's a breakdown of its main purposes:

1. **Appearance of the Settings Page**: The `SettingsPage` component handles the overall layout and structure of the settings page. It defines how the page is organized, including title and subtitle, more actions dropdown, CTAs, content basic layout and widgets. Also, it is responsible for rendering loading skeletons, error state, displaying notifications and other.

2. **Handling Fetch**: The `SettingsPage` component typically integrates with a fetch mechanism (e.g., an API call) to retrieve data related to the settings being displayed on the page. This integration is done through the `fetch` function passed as a prop to the [`useSettingsPage`](./?path=/story/base-components-pages-settings-page-hooks-usesettingspage--usesettingspage) hook, allowing the component to retrieve the necessary data for rendering. You can use the [`useSettings`](./?path=/story/base-components-pages-settings-page-hooks-usesettings--usesettings)  to access a fetched settings.

3. **Handling Save and Validation Logic**: The [useForm](https://react-hook-form.com/docs/useform)  hook from [react-hook-form](https://react-hook-form.com/) is used to create a form object, and the `onSave` function passed as a prop to the [`useSettingsPage`](./?path=/story/components-page-settingspage-hooks-usesettingspage--usesettingspage) hook, is triggered when the user initiates a save action.

4. **Easy integration with open platform capabilities**: You can create a container in DevCenter and WixPatterns will fetch and present relevant extensions. You can use [`MoreActions`](./?path=/story/features-actions-more-actions--moreactions) to get Menu Item Extensions.

In summary, the ``SettingsPage`` component and the associated [`useSettingsPage`](./?path=/story/base-components-pages-settings-page-hooks-usesettingspage--usesettingspage) hook together provide a streamlined approach to rendering, fetching, saving, and validating data for a specific settings within an application.


## Examples

### Variations

### Basic

This example demonstrates a basic usage of the `SettingsPage`.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
} from '@wix/patterns';
import { useForm } from '@wix/patterns/form';
import { Box, Card, Text } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

interface FormValues {
  name?: string;
}

function Basic() {
  const form = useForm<FormValues>();
  const state: SettingsPageState<MySettings, FormValues> = useSettingsPage<
    MySettings,
    FormValues
  >({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      Promise.resolve({
        settings: {
          id: 'id',
          name: 'Settings Name',
          createdAt: new window.Date(),
          updatedAt: new window.Date(),
        },
      }),
  });

  const settings = useSettings<MySettings, FormValues>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Name Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>{settings?.name}</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### 2 Columns

This example illustrates the use of both main and additional content.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
} from '@wix/patterns';
import { useForm } from '@wix/patterns/form';
import { Box, Card, Text } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

function TwoColumns() {
  const form = useForm();
  const state: SettingsPageState<MySettings> = useSettingsPage<MySettings>({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      Promise.resolve({
        settings: {
          id: 'id',
          name: 'Settings Name',
          createdAt: new window.Date(),
          updatedAt: new window.Date(),
        },
      }),
  });

  const settings = useSettings<MySettings>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Main Content Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>Stretched to 8/12 of available space.</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
        <SettingsPage.AdditionalContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Side Content Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>Stretched to 4/12 of available space.</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.AdditionalContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### Loading

This sample illustrates the skeleton state of an settings page based on the provided structure.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettingsPage,
} from '@wix/patterns';
import { useForm } from '@wix/patterns/form';
import { Box, Card, Text } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

interface FormFields {
  id: string;
  name?: string;
}

function Loading() {
  const form = useForm<FormFields>();
  const state: SettingsPageState<MySettings, FormFields> = useSettingsPage<
    MySettings,
    FormFields
  >({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      new Promise((resolve) => {
        const dateToResolve = new window.Date(window.Date.now());
        // keep waiting :)
        if (dateToResolve.getFullYear() === 2500) {
          resolve({
            settings: {
              id: 'id',
              name: 'My Settings',
              createdAt: new window.Date(),
              updatedAt: new window.Date(),
            },
          });
        }
      }),
  });

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: 'Settings Page' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Card" />
            <Card.Divider />
            <Card.Content>
              <Box>
                <Text>Main Card #1</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Card" />
            <Card.Divider />
            <Card.Content>
              <Box>
                <Text>Main Card #2</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
        <SettingsPage.AdditionalContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Card" />
            <Card.Divider />
            <Card.Content>
              <Box>
                <Text>Side Card #1</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Card" />
            <Card.Divider />
            <Card.Content>
              <Box>
                <Text>Side Card #2</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.AdditionalContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### Error on Fetch

This example demonstrates the error state when fetching an settings fails.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
} from '@wix/patterns';
import { useForm } from '@wix/patterns/form';
import { Box, Card, Text } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

function FetchError() {
  const form = useForm();
  const state: SettingsPageState<MySettings> = useSettingsPage<MySettings>({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      new Promise((_, reject) =>
        window.setTimeout(() => reject(new Error('Something went wrong')), 1000),
      ),
  });

  const settings = useSettings<MySettings>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Main Content Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>Stretched to 8/12 of available space.</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
        <SettingsPage.AdditionalContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Side Content Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>Stretched to 4/12 of available space.</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.AdditionalContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### Error on Save

This example demonstrates the error toast when saving an settings fails.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
} from '@wix/patterns';
import { useForm } from '@wix/patterns/form';
import { Box, Card, Text } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

function SaveError() {
  const form = useForm();
  const state: SettingsPageState<MySettings> = useSettingsPage<MySettings>({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: () => Promise.reject(new Error('Something went wrong')),
    saveErrorToast: (e, { retry }) => ({
      message: 'oops.. unknown error',
      action: { text: 'Retry', onClick: retry },
    }),
    fetch: () =>
      Promise.resolve({
        settings: {
          id: 'id',
          name: 'Settings Name',
          createdAt: new window.Date(),
          updatedAt: new window.Date(),
        },
      }),
  });

  const settings = useSettings<MySettings>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Card" />
            <Card.Divider />
            <Card.Content>
              <Box gap="SP2" direction="vertical">
                <Text>I'm a card</Text>
              </Box>
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### Developer Examples

### Input initialization with useController

This demonstrates how to use [useController](https://react-hook-form.com/docs/usecontroller) with the default value set at the `Card.Content` level. Note that the `state.settings` value is initialized following the Card skeleton loader. To obtain the initial value, it must be accessed within the Card.Content child, as illustrated in the following example. Accessing it before or at a higher level will result in an `undefined` value.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
  useSettingsPageContext,
} from '@wix/patterns';
import { useController, useForm } from '@wix/patterns/form';
import { Box, Card, FormField, Input } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

interface FormValues {
  name?: string;
}

function useControllerExample() {
  const form = useForm<FormValues>();
  const state: SettingsPageState<MySettings, FormValues> = useSettingsPage<
    MySettings,
    FormValues
  >({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      Promise.resolve({
        settings: {
          id: 'id',
          name: 'Settings Name',
          createdAt: new window.Date(),
          updatedAt: new window.Date(),
        },
      }),
  });

  // Usually we will extract this component to a separate file
  const CardContent = () => {
    const pageState = useSettingsPageContext<MySettings, FormValues>();
    const name = useController({
      name: 'name',
      control: pageState.form.control,
      defaultValue: pageState.settings?.name,
      rules: {
        required: { value: true, message: 'This field is required!' },
      },
    });

    return (
      <Box gap="SP2" direction="vertical">
        <FormField label="Name">
          <Input
            dataHook="name-input"
            inputRef={name.field.ref}
            value={name.field.value}
            onChange={name.field.onChange}
            onBlur={name.field.onBlur}
          />
        </FormField>
      </Box>
    );
  };

  const settings = useSettings<MySettings, FormValues>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Name Card" />
            <Card.Divider />
            <Card.Content>
              <CardContent />
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

---

### Input initialization with Controller

This demonstrates how to use [Controller](https://react-hook-form.com/docs/usecontroller/controller) where you can easily access the settings by using `useSettings`. This method allows for accessing the settings much earlier in the process. As a result, the default value will be dynamically updated as soon as the settings itself is updated, as shown in the example below.

```tsx
import React from 'react';
import {
  SettingsPage,
  SettingsPageState,
  useSettings,
  useSettingsPage,
} from '@wix/patterns';
import { Controller, useForm } from '@wix/patterns/form';
import { Card, FormField, Input } from '@wix/design-system';

interface MySettings {
  id: string;
  name?: string;
  createdAt: Date;
  updatedAt: Date;
}

interface FormValues {
  name?: string;
}

function ControllerExample() {
  const form = useForm<FormValues>();
  const state: SettingsPageState<MySettings, FormValues> = useSettingsPage<
    MySettings,
    FormValues
  >({
    form,
    onCancel: async () => console.log('Cancel clicked!'),
    onSave: async () => {
      const formValues = form.getValues();
      console.log('formValues', formValues); // Save updated settings
    },
    saveErrorToast: (e) => 'Failed to save',
    fetch: () =>
      Promise.resolve({
        settings: {
          id: 'id',
          name: 'Settings Name',
          createdAt: new window.Date(),
          updatedAt: new window.Date(),
        },
      }),
  });

  const settings = useSettings<MySettings, FormValues>(state);

  return (
    <SettingsPage state={state}>
      <SettingsPage.Header title={{ text: settings?.name ?? '' }} />
      <SettingsPage.Content>
        <SettingsPage.MainContent>
          <SettingsPage.Card minHeight="24px">
            <Card.Header title="Name Card" />
            <Card.Divider />
            <Card.Content>
              <Controller
                control={form.control}
                name="name"
                defaultValue={settings?.name}
                rules={{
                  required: { value: true, message: 'This field is required!' },
                }}
                render={({
                  field: { onChange, onBlur, value, ref },
                  fieldState: { invalid, error },
                }) => (
                  <FormField
                    label="Name"
                    status={invalid ? 'error' : undefined}
                    statusMessage={error?.message}
                  >
                    <Input
                      dataHook="name-input"
                      inputRef={ref}
                      value={value}
                      onChange={onChange}
                      onBlur={onBlur}
                    />
                  </FormField>
                )}
              />
            </Card.Content>
          </SettingsPage.Card>
        </SettingsPage.MainContent>
      </SettingsPage.Content>
    </SettingsPage>
  );
}
```

## API

### Extends

[Page](https://www.docs.wixdesignsystem.com/?path=/story/components-layout-page--page)

### Props

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `state` | `SettingsPageState<T, V>` | Yes | - | SettingsPageState instance created with the `useSettingsPage` hook |
| `children` | `ReactNode` | No | - | Accepts compound components as child items: - `<Page.Header/>` - `<Page.Content/>` - `<Page.Tail/>` - `<Page.Section/>` - `<Page.Footer/>` |
| `dataHook` | `string` | No | - | Applies a data-hook HTML attribute that can be used in the tests |
| `backgroundImageUrl` | `string` | No | - | Provides a link to the background image source (URL). Image will be displayed at the top of a page, underneath the \<Page.Header/>. |
| `maxWidth` | `number` | No | - | Sets the maximum width of the page (paddings excluded) |
| `minWidth` | `number` | No | - | Sets the minimum width of the page (paddings excluded). When `mobile` is enabled on `<WixDesignSystemProvider/>`, the default minimum width is removed, but an explicitly provided value is still applied. |
| `height` | `string` | No | - | Sets the height of the page in pixels (px), viewport height (vh), etc. |
| `sidePadding` | `number` | No | - | Sets the side paddings of a page |
| `className` | `string` | No | - | Specifies a CSS class name to be appended to the component’s root element. |
| `gradientClassName` | `string` | No | - | Specifies a CSS class name that defines a gradient background for \<Page.Header/> |
| `scrollableContentRef` | `((ref: HTMLElement \| null) => void)` | No | - | Defines a reference called together with page scrollable content ref after page mount. <br/> **Note** - if you need this ref only for listening to scroll events on the scrollable content then use this prop instead: `scrollProps = {onScrollChanged/onScrollAreaChanged}` |
| `zIndex` | `number` | No | - | Specifies the stack order (z-index) of a page |
| `horizontalScroll` | `boolean` | No | - | Enables page content to scroll horizontally when its width exceed maximum allowed width |
| `scrollProps` | `ScrollableContainerCommonProps` | No | - | Pass properties related to the scrollable content of the page. <br/> **onScrollAreaChanged** - defines a handler function for scroll area changes. Function is triggered when the user scrolls to a different area of the scrollable content. See signature for possible areas: `function({area: {y: AreaY, x: AreaX}, target: HTMLElement}) => void` `AreaY`: top \| middle \| bottom \| none `AreaX`: start \| middle \| end \| none (not implemented yet) <br/> **onScrollAreaChanged** - defines a general handler for scroll changes with throttling (100ms): `function({target: HTMLElement}) => void` |

## BI

### Settings Page Events

| Event                                                                                                     | Description                                          |
|-----------------------------------------------------------------------------------------------------------|------------------------------------------------------|
| [144:150](https://bo.wix.com/data-tools/bi-catalog-app/event/144:150) | Sent on `Cancel` / `Save` click with `isValid` flag. |
| [144:149](https://bo.wix.com/data-tools/bi-catalog-app/event/144:149) | Sent on unsuccessful `Save`.                         |
| [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:174](https://bo.wix.com/data-tools/bi-catalog-app/event/144:174) | Sent on navigation with a dirty state.               |



### 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)


