---
title: Customize the Sign Up Form
description: Customize the sign up form by adding new fields or modifying existing ones.
sidebar:
  order: 30
---

## Before you start

The next instructions assume that you have a working application that uses **SuperTokens** for authentication.
If not, please refer to the [quickstart guide](/quickstart#1-integrate-the-frontend-sdk) and then return here.

<UITypeSwitch />


## Add extra fields

To include more fields in the sign up form, you need to first update both the frontend and backend configuration.
Then, when the sing up payload arrives on the backend, you should add a way to persist those values.

### 1. Add the new fields to the UI

<VariantContent storageKey="ui-type" value="custom">


You first need to add the new fields to your sign up interface.
Given that you are using a custom implementation, the steps vary based on your code.
After you have updated the form, ensure that the submit action follows the next example.


<CodeGroup group="frontend-custom-ui">
<Tab title="Web" value="web">
<DependentContent group="install-method" label="Installation method">
<ContentOption title="npm" value="npm">
```tsx
import { signUp } from "supertokens-web-js/recipe/emailpassword";

async function signUpClicked(email: string, password: string, name: string, age: number, country: string) {
  let response = await signUp({
    formFields: [
      {
        id: "email",
        value: email,
      },
      {
        id: "password",
        value: password,
      },
      {
        id: "name",
        value: name,
      },
      {
        id: "age",
        value: age + "",
      },
      {
        id: "country",
        value: country,
      },
    ],
  });
  // ... rest of the code
}
```
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
async function signUpClicked(email: string, password: string, name: string, age: number, country: string) {
  let response = await supertokensEmailPassword.signUp({
    formFields: [
      {
        id: "email",
        value: email,
      },
      {
        id: "password",
        value: password,
      },
      {
        id: "name",
        value: name,
      },
      {
        id: "age",
        value: age + "",
      },
      {
        id: "country",
        value: country,
      },
    ],
  });
  // ... rest of the code
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Mobile" value="mobile">
```bash
curl --location --request POST '<YOUR_API_DOMAIN>/auth/signup'
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
    "formFields": [{
        "id": "email",
        "value": "john@example.com"
    }, {
        "id": "password",
        "value": "somePassword123"
    }, {
        id: "name",
        value: "John Doe"
    }, {
        id: "age",
        value: 27
    }, {
        id: "country",
        value: "USA"
    }]
}'
```
</Tab>
</CodeGroup>



</VariantContent>

<VariantContent storageKey="ui-type" value="prebuilt">


<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "name",
              label: "Full name",
              placeholder: "First name and last name",
            },
            {
              id: "age",
              label: "Your age",
              placeholder: "How old are you?",
            },
            {
              id: "country",
              label: "Your country",
              placeholder: "Where do you live?",
              optional: true,
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "name",
              label: "Full name",
              placeholder: "First name and last name",
            },
            {
              id: "age",
              label: "Your age",
              placeholder: "How old are you?",
            },
            {
              id: "country",
              label: "Your country",
              placeholder: "Where do you live?",
              optional: true,
            },
          ],
        },
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>


#### Create custom components

By default, the new fields use `input` elements.
To enable more complex fields you can create your own custom components.

:::note
You may need to disable the Shadow DOM if you're integrating with a different component library that requires you to import its own CSS. For instance, some component libraries, such as [react-international-phone](https://github.com/goveo/react-international-phone), might expect you to include their CSS alongside their components. For more information, refer to [Disable use of shadow DOM](/references/frontend-sdks/prebuilt-ui/shadow-dom).
:::


Set the `inputComponent` property for each field that you want to customize.


<DependentContent passive group="frontend-prebuilt-ui">
<ContentOption title="Angular" value="angular">
:::warning[This is not applicable for non React apps. You have to create your own custom UI instead.]
:::
</ContentOption>
</DependentContent>

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "select-dropdown",
              label: "Select Dropdown",
              inputComponent: ({ value, name, onChange }) => (
                <div data-supertokens="inputContainer">
                  <div data-supertokens="inputWrapper ">
                    <select
                      style={{
                        border: "unset",
                        borderRadius: "6px",
                        height: "32px",
                        backgroundColor: "#fafafa",
                        color: "#757575",
                        letterSpacing: "1.2px",
                        fontSize: "14px",
                        width: "100%",
                        marginRight: "25px",
                        padding: "1px 0 1px 10px",
                      }}
                      value={value}
                      name={name}
                      onChange={(e) => onChange(e.target.value)}
                    >
                      <option value="" disabled hidden>
                        Select an option
                      </option>
                      <option value="option 1">Option 1</option>
                      <option value="option 2">Option 2</option>
                      <option value="option 3">Option 3</option>
                    </select>
                  </div>
                </div>
              ),
              optional: true,
            },
            {
              id: "terms",
              label: "",
              optional: false,
              nonOptionalErrorMsg: "You must accept the terms and conditions",
              inputComponent: ({ name, onChange }) => (
                <div
                  style={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "left",
                    marginBottom: " -12px",
                  }}
                >
                  <input name={name} type="checkbox" onChange={(e) => onChange(e.target.checked.toString())}></input>
                  <span style={{ marginLeft: 5 }}>
                    I agree to the{" "}
                    <a href="https://supertokens.com/legal/terms-and-conditions" data-supertokens="link">
                      Terms and Conditions
                    </a>
                  </span>
                </div>
              ),
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">

</Tab>
</CodeGroup>


</VariantContent>

### 2. Include the extra fields in the backend configuration

Change the **Backend SDK** initialization call to ensure that the system processes the new fields when a new user registers.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  supertokens: {
    connectionURI: "...",
  },
  recipeList: [
    EmailPassword.init({
      signUpFeature: {
        formFields: [
          {
            id: "name",
          },
          {
            id: "age",
          },
          {
            id: "country",
            optional: true,
          },
        ],
      },
    }),
    Session.init({
      /* ... */
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/emailpassword"
	"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	countryOptional := true
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "name",
						},
						{
							ID: "age",
						},
						{
							ID:       "country",
							Optional: &countryOptional,
						},
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword, session
from supertokens_python.recipe.emailpassword import InputFormField

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),

    framework='...',
    recipe_list=[
        emailpassword.init(
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[InputFormField(id='name'), InputFormField(id='age'), InputFormField(id='country', optional=True)]
            )
        ),
        session.init()
    ]
)
```
</Tab>
</CodeGroup>

### 3. Save the values after a successful sign up

Use the `signUpPOST` API function to process the field values and persist them.

:::warning
**SuperTokens** does not store custom form fields. You can either save them in your database or use the [User Metadata feature ](/post-authentication/user-management/user-metadata).
:::

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  supertokens: {
    connectionURI: "...",
  },
  recipeList: [
    EmailPassword.init({
      override: {
        apis: (originalImplementation) => {
          return {
            ...originalImplementation,
            signUpPOST: async function (input) {
              if (originalImplementation.signUpPOST === undefined) {
                throw Error("Should never come here");
              }

              // First we call the original implementation of signUpPOST.
              let response = await originalImplementation.signUpPOST(input);

              // Post sign up response, we check if it was successful
              if (response.status === "OK") {
                // These are the input form fields values that the user used while signing up
                let formFields = input.formFields;
              }
              return response;
            },
          };
        },
      },
    }),
    Session.init({
      /* ... */
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/emailpassword"
	"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				Override: &epmodels.OverrideStruct{
					APIs: func(originalImplementation epmodels.APIInterface) epmodels.APIInterface {
						// First we copy the original implementation func
						originalSignUpPOST := *originalImplementation.SignUpPOST

						(*originalImplementation.SignUpPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.SignUpPOSTResponse, error) {
							resp, err := originalSignUpPOST(formFields, tenantId, options, userContext)
							if err != nil {
								return epmodels.SignUpPOSTResponse{}, err
							}

							if resp.OK != nil {
								// sign up was successful

								// TODO: You can now read the formFields from the input params

							}

							return resp, err
						}

						return originalImplementation
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword, session
from supertokens_python.recipe.emailpassword.interfaces import (
    APIInterface,
    APIOptions,
    SignUpPostOkResult,
)
from supertokens_python.recipe.emailpassword.types import FormField
from typing import List, Dict, Any, Union
from supertokens_python.recipe.session.interfaces import SessionContainer


def override_email_password_apis(original_implementation: APIInterface):
    original_sign_up_post = original_implementation.sign_up_post

    async def sign_up_post(
        form_fields: List[FormField],
        tenant_id: str,
        session: Union[SessionContainer, None],
        should_try_linking_with_session_user: Union[bool, None],
        api_options: APIOptions,
        user_context: Dict[str, Any],
    ):
        # First we call the original implementation of sign_up_post.
        response = await original_sign_up_post(
            form_fields,
            tenant_id,
            session,
            should_try_linking_with_session_user,
            api_options,
            user_context,
        )

        # Post sign up response, we check if it was successful
        if isinstance(response, SignUpPostOkResult):
            pass
            # TODO: use the input form fields values for custom logic

        return response

    original_implementation.sign_up_post = sign_up_post
    return original_implementation


init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework="...",
    recipe_list=[
        emailpassword.init(
            override=emailpassword.InputOverrideConfig(
                apis=override_email_password_apis
            )
        ),
        session.init(),
    ],
)
```
</Tab>
</CodeGroup>

---

## Customize each form field

<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
This section is not relevant for custom UI, as you create your own UI and already have control over the form fields.
:::

</VariantContent>

<VariantContent storageKey="ui-type" value="prebuilt">

### Modify labels and placeholders

To change the labels and placeholders of the fields, update the `formFields` property, in the recipe configuration.

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "customFieldName",
              placeholder: "Custom value",
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "customFieldName",
              placeholder: "Custom value",
            },
          ],
        },
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>


### Set default values

Add a `getDefaultValue` option to the `formFields` configuration to set default values.
Keep in mind that the function needs to return a string.

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "Your Email",
              getDefaultValue: () => "john.doe@gmail.com",
            },
            {
              id: "name",
              label: "Full name",
              getDefaultValue: () => "John Doe",
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "Your Email",
              getDefaultValue: () => "john.doe@gmail.com",
            },
            {
              id: "name",
              label: "Full name",
              getDefaultValue: () => "John Doe",
            },
          ],
        },
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>


### Change the optional error message

When you try to submit the login form without filling in the required fields, the UI, by default, shows an error stating that the `Field is not optional`.
To customize this message set the `nonOptionalErrorMsg` property to a custom string.

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "Your Email",
              placeholder: "Email",
              nonOptionalErrorMsg: "Please add your email",
            },
            {
              id: "name",
              label: "Full name",
              placeholder: "Name",
              nonOptionalErrorMsg: "Full name is required",
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "Your Email",
              placeholder: "Email",
              nonOptionalErrorMsg: "Please add your email",
            },
            {
              id: "name",
              label: "Full name",
              placeholder: "Name",
              nonOptionalErrorMsg: "Full name is required",
            },
          ],
        },
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>


### Change the field order

To customize the order of fields in your sign up form, override the `EmailPasswordSignUpForm` component.
Use the next example as a reference.

<DependentContent passive group="frontend-prebuilt-ui">
<ContentOption title="Angular" value="angular">
:::warning[This is not applicable for non React apps. You have to create your own custom UI instead.]

:::
</ContentOption>
</DependentContent>

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
<DependentContent group="react-router" label="Do you use react-router-dom?">
<ContentOption title="With React Router" value="yes">
```tsx
import React from "react";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { EmailPasswordComponentsOverrideProvider } from "supertokens-auth-react/recipe/emailpassword";

function App() {
  return (
    <SuperTokensWrapper>
      <EmailPasswordComponentsOverrideProvider
        components={{
          EmailPasswordSignUpForm_Override: ({ DefaultComponent, ...props }) => {
            return (
              <DefaultComponent
                {...props}
                formFields={[
                  props.formFields.find(({ id }) => id === "name")!,
                  props.formFields.find(({ id }) => id === "email")!,
                  props.formFields.find(({ id }) => id === "password")!,
                ]}
              />
            );
          },
        }}
      >
        {/* Rest of the JSX */}
      </EmailPasswordComponentsOverrideProvider>
    </SuperTokensWrapper>
  );
}
export default App;
```
</ContentOption>
<ContentOption title="Without React Router" value="no">
```tsx
import React from "react";
import { SuperTokensWrapper } from "supertokens-auth-react";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { EmailPasswordComponentsOverrideProvider } from "supertokens-auth-react/recipe/emailpassword";
import { getRoutingComponent, canHandleRoute } from "supertokens-auth-react/ui";

function App() {
  if (canHandleRoute([EmailPasswordPreBuiltUI])) {
    return (
      <EmailPasswordComponentsOverrideProvider
        components={{
          EmailPasswordSignUpForm_Override: ({ DefaultComponent, ...props }) => {
            return (
              <DefaultComponent
                {...props}
                formFields={[
                  props.formFields.find(({ id }) => id === "name")!,
                  props.formFields.find(({ id }) => id === "email")!,
                  props.formFields.find(({ id }) => id === "password")!,
                ]}
              />
            );
          },
        }}
      >
        {getRoutingComponent([EmailPasswordPreBuiltUI])}
      </EmailPasswordComponentsOverrideProvider>
    );
  }
  return <SuperTokensWrapper>{/* Rest of the JSX */}</SuperTokensWrapper>;
}
export default App;
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Angular" value="angular">

</Tab>
</CodeGroup>


</VariantContent>


---

## Change field validators


### 1. Update the frontend configuration

<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
For your custom UI, you have to implement field validation checking yourself.
Note that you need to also update the backend validation to ensure a complete flow.
Check the next section for more details.
:::
</VariantContent>

<VariantContent storageKey="ui-type" value="prebuilt">

Add a `validate` method to any of your `formFields`.
The following example shows how to add age verification to the form:

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "name",
              label: "Full name",
              placeholder: "First name and last name",
            },
            {
              id: "age",
              label: "Your age",
              placeholder: "How old are you?",
              optional: true,

              /* Validation method to make sure that age is above 18 */
              validate: async (value) => {
                if (parseInt(value) > 18) {
                  return undefined; // means that there is no error
                }
                return "You must be over 18 to register";
              },
            },
            {
              id: "country",
              label: "Your country",
              placeholder: "Where do you live?",
              optional: true,
            },
          ],
        },
      },
    }),
    Session.init(),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "name",
              label: "Full name",
              placeholder: "First name and last name",
            },
            {
              id: "age",
              label: "Your age",
              placeholder: "How old are you?",
              optional: true,

              /* Validation method to make sure that age is above 18 */
              validate: async (value) => {
                if (parseInt(value) > 18) {
                  return undefined; // means that there is no error
                }
                return "You must be over 18 to register";
              },
            },
            {
              id: "country",
              label: "Your country",
              placeholder: "Where do you live?",
              optional: true,
            },
          ],
        },
      },
    }),
    supertokensUISession.init(),
  ],
});
```
</Tab>
</CodeGroup>

</VariantContent>

### 2. Update the backend configuration

Add `validate` functions to each of the form fields, in the backend SDK initialization call.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  supertokens: {
    connectionURI: "...",
  },
  recipeList: [
    EmailPassword.init({
      signUpFeature: {
        formFields: [
          {
            id: "name",
          },
          {
            id: "age",
            /* Validation method to make sure that age >= 18 */
            validate: async (value, tenantId) => {
              if (parseInt(value) >= 18) {
                return undefined; // means that there is no error
              }
              return "You must be over 18 to register";
            },
          },
          {
            id: "country",
            optional: true,
          },
        ],
      },
    }),
    Session.init({}),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"strconv"

	"github.com/supertokens/supertokens-golang/recipe/emailpassword"
	"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	countryOptional := true
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "name",
						},
						{
							ID: "age",
							Validate: func(value interface{}, tenantId string) *string {
								age, _ := strconv.Atoi(value.(string))
								if age >= 18 {
									// return nil to indicate success
									return nil
								}
								err := "You must be over 18 to register"
								return &err
							},
						},
						{
							ID:       "country",
							Optional: &countryOptional,
						},
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword import InputFormField
from typing import Any

async def validate_age(value: Any, tenant_id: str):
    # Validation method to make sure that age >= 18
    if int(value) >= 18:
        return None # means that there is no error
    return 'You must be over 18 to register'

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),

    framework='...',
    recipe_list=[
        emailpassword.init(
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[
                    InputFormField(id='name'),
                    InputFormField(id='age', validate=validate_age),
                    InputFormField(id='country', optional=True)
                ]
            )
        )
    ]
)
```
</Tab>
</CodeGroup>

:::info[Multi-tenancy]

Notice the `tenantId` argument passed into the `validate` function. Using that, you can define custom logic per tenant. For example, you can define different password policies for different tenants.

:::

### Change the default email and password validators

By default, SuperTokens adds an email and a password validator to the sign up form.
- The default email validator makes sure that the provided email is in the correct email format.
- The default password validator makes sure that the provided password:
   - has a minimum of 8 characters.
   - contains at least one lowercase character
   - contains at least one number

To add your own validators follow the steps described initially in this section.

:::note[- The email validator that you define for **sign up** is also applied automatically to **sign in**.]
- The password validator that you define for **sign up** is also applied automatically to **reset password** forms.
:::

Here is an example of what you need to change.


##### 1. Update the frontend configuration

<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable]
For your custom UI, you have to implement field validation checking yourself.
Note that you need to also update the backend validation to ensure a complete flow.
Check the next section for more details.
:::

</VariantContent>

<VariantContent storageKey="ui-type" value="prebuilt">
<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    EmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "...",
              validate: async (value) => {
                // Your own validation returning a string or undefined if no errors.
                return "...";
              },
            },
            {
              id: "password",
              label: "...",
              validate: async (value) => {
                // Your own validation returning a string or undefined if no errors.
                return "...";
              },
            },
          ],
        },
      },
    }),
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    supertokensUIEmailPassword.init({
      signInAndUpFeature: {
        signUpForm: {
          formFields: [
            {
              id: "email",
              label: "...",
              validate: async (value) => {
                // Your own validation returning a string or undefined if no errors.
                return "...";
              },
            },
            {
              id: "password",
              label: "...",
              validate: async (value) => {
                // Your own validation returning a string or undefined if no errors.
                return "...";
              },
            },
          ],
        },
      },
    }),
  ],
});
```
</Tab>
</CodeGroup>
</VariantContent>

##### 1. Update the backend configuration

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  supertokens: {
    connectionURI: "...",
  },
  recipeList: [
    EmailPassword.init({
      signUpFeature: {
        formFields: [
          {
            id: "email",
            validate: async (value, tenantId) => {
              // Your own validation returning a string or undefined if no errors.
              return "...";
            },
          },
          {
            id: "password",
            validate: async (value, tenantId) => {
              // Your own validation returning a string or undefined if no errors.
              return "...";
            },
          },
        ],
      },
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/emailpassword"
	"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			emailpassword.Init(&epmodels.TypeInput{
				SignUpFeature: &epmodels.TypeInputSignUp{
					FormFields: []epmodels.TypeInputFormField{
						{
							ID: "email",
							Validate: func(value interface{},  tenantId string) *string {
								// Your own validation returning a string or nil if no errors.
								return nil
							},
						},
						{
							ID: "password",
							Validate: func(value interface{},  tenantId string) *string {
								// Your own validation returning a string or nil if no errors.
								return nil
							},
						},
					},
				},
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="This example omits surrounding application and SuperTokens configuration."
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword import InputFormField
from typing import Any

async def validate_password(value: Any, tenant_id: str):
    pass # TODO

async def validate_email(value: Any, tenant_id: str):
    pass # TODO

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...',
    recipe_list=[
        emailpassword.init(
            sign_up_feature=emailpassword.InputSignUpFeature(
                form_fields=[
                    InputFormField(id='password', validate=validate_password),
                    InputFormField(id='email', validate=validate_email)
                ]
            )
        )
    ]
)
```
</Tab>
</CodeGroup>

---

## Add terms of service and privacy policy links

<VariantContent storageKey="ui-type" value="prebuilt">

To add "Terms of service" and "Privacy policy" links to your sign up page add the links in the frontend SDK initialization call.
Based on the provided configuration the data renders in the following way:
- Provided both links: "By signing up, you agree to the [Terms of Service](#add-terms-of-service-and-privacy-policy-links) and [Privacy Policy](#add-terms-of-service-and-privacy-policy-links)"
- Provided only Terms of service link: "By signing up, you agree to the [Terms of Service](#add-terms-of-service-and-privacy-policy-links)"
- Provided only Privacy policy link: "By signing up, you agree to the [Privacy Policy](#add-terms-of-service-and-privacy-policy-links)"

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import SuperTokens from "supertokens-auth-react";

SuperTokens.init({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  termsOfServiceLink: "https://example.com/terms-of-service",
  privacyPolicyLink: "https://example.com/privacy-policy",
  recipeList: [
    /* ... */
  ],
});
```
</Tab>
<Tab title="Angular" value="angular">
```tsx check=false reason="This example omits surrounding application and SuperTokens configuration."
// this goes in the auth route config of your frontend app (once the pre-built UI script has been loaded)

supertokensUIInit({
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  termsOfServiceLink: "https://example.com/terms-of-service",
  privacyPolicyLink: "https://example.com/privacy-policy",
  recipeList: [
    /* ... */
  ],
});
```
</Tab>
</CodeGroup>

</VariantContent>
<VariantContent storageKey="ui-type" value="custom">

:::warning[Not applicable since you do not use the pre-built UI.]
:::

</VariantContent>

---


## See also

<CardGroup cols={3}>
  <Card title="Password Hashing" href="/authentication/email-password/password-hashing" />
  <Card title="Hooks and overrides" href="/authentication/email-password/hooks-and-overrides" />
  <Card title="Username login" href="/authentication/email-password/implement-username-login" />
  <Card title="Password reset" href="/authentication/email-password/password-reset" />
</CardGroup>
