> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-actions-triggers-prototype.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Learn how you can render Forms using Actions.

# Render Forms using Actions

Once you create a form, you can render it with [Actions](/docs/customize/actions) using the `api.prompt.render()` method:

```javascript lines theme={null}
// Example using the post-login trigger

exports.onExecutePostLogin = async (event, api) => {
  api.prompt.render(':form_id');
}

exports.onContinuePostLogin = async (event, api) => {
  // Add your logic after completing the form
}
```

Replace form\_id with the ID of the Form. You can locate the ID in the URL of the Form, for example:  `ap_pUMG...` or select it from the Form editor **Render** tab.
In the [Actions Code editor](/docs/customize/actions/write-your-first-action#create-an-action), you can define the business logic to decide when and how to render the Form.

```javascript lines theme={null}
exports.onExecutePostLogin = async (event, api) => {

  // Only render the form if user is missing company_name metadata
  if (!event.user.user_metadata.company_name) {
    api.prompt.render(':form_id');
  }
}

exports.onContinuePostLogin = async (event, api) => {
  // Add your logic after completing the form
}
```

To learn more these objects, review:

* [Event Object](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-event-object): Learn about the Event objects and properties.
* [API object](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-api-object): Learn about the API objects and methods.

## Populate values for existing fields and hidden fields (client-side)

You can populate values for existing fields and hidden fields using the `fields` property as a second argument in your render method.

In the example below, the value `Jane` populates `first_name` field.

```javascript lines theme={null}
exports.onExecutePostLogin = async (event, api) => {
  api.prompt.render(':form_id', {
    fields: {
      first_name: 'Jane',
    }
  });
}

exports.onContinuePostLogin = async (event, api) => {
  // Add your logic after completing the form
}
```

<Warning>
  Do not populate sensitive information, populated values are exposed to the browser (client-side).
</Warning>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Make sure that the field `ID` referenced in the second argument is available client-side in your [nodes and components](/docs/customize/forms/nodes-and-components); otherwise, the value will not populate.
</Callout>

## Inject custom data with shared variables (server-side)

You can inject server-side variables using the `vars` property as a second argument in your render method. This can be used to inject sensitive information without exposing it to the client-side.

In the example below, the value `123456789` populates the variable `external_user_id`.

```javascript lines theme={null}
exports.onExecutePostLogin = async (event, api) => {
  api.prompt.render(':form_id', {
    vars: {
      external_user_id: '123456789',
    }
  });
}

exports.onContinuePostLogin = async (event, api) => {
  // Add your logic after completing the form
}
```

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Shared variables `{{vars.*}}` do not have data type restrictions.

  Shared variables `{{vars.*}}` are always automatically masked.

  Unlike when populating `{{fields.*}}` variables, the `{{vars.*}}` property does not need to exist in your form before injecting values.

  You can use the `{{vars.*}}` property to reference shared variables in your Forms and its linked Flows. To learn more, read [Variables](/docs/customize/forms/variables).
</Callout>

## Fields and shared variables data in Actions

Fields and shared variables data collected, in your forms, is automatically available with the `event.prompt` in the resume function of the current action:

* The `id` property, with the prompt ID you're rendering.
* The `fields` object, which contains all your fields and hidden fields data.
* The `vars` object, which contains all your shared variables data.

<Accordion title="Example of event.prompt object">
  ```json lines theme={null}
  {
    "id": "ap_fuVuFiiQWN3mTEujWTy966",
    "fields": {
      "first_name": "Jane",
      "company_name": "Okta"
    },
    "vars": {
      "external_crm_uuid": "f8f32e6f-2329-49bd-bf21-fa8b0bea2652",
      "api_hostname": "api.example.com"
    }
  }
  ```
</Accordion>

In the example below, the `api.user.setUserMetadata` populates the `user_metadata` `company_name` attribute with the `event.prompt.fields.company_name` property that was collected from your form.

```javascript lines theme={null}
exports.onExecutePostLogin = async (event, api) => {
  api.prompt.render(':form_id');
}

exports.onContinuePostLogin = async (event, api) => {
  api.user.setUserMetadata('company_name', event.prompt.fields.company_name);
}
```

## Restrictions and limitations

* You cannot [redirect](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/redirect-with-actions) a user and render a form in the same Action. If you need to use both, consider using different Actions.
* You can only render one form per Action. If you need to render more than one form, you need to render the forms in different Actions.
* The `fields` property size limit is 24 KB.
* The `api.prompt.render()` method is available in the following triggers:

  * [post-login](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-api-object)
  * [post-challenge](/docs/customize/actions/explore-triggers/password-reset-triggers/post-challenge-trigger/post-challenge-api-object)
