> This is Payabli documentation. For a complete page index, fetch https://docs.payabli.com/llms.txt — append .md to any page URL for lightweight markdown. For section-level indexes, query parameters, and other AI-optimized access methods, see https://docs.payabli.com/ai-agents.md

# Embedded components framework integrations

> Learn how to use Payabli's embedded components with front-end frameworks like React and Vue

You can use Payabli's embedded components in a React, Vue, or React Native application following the same configuration patterns as in a vanilla JavaScript application.

## React

Visit the [React Integration Example](https://github.com/payabli/examples/tree/main/react-integration) to see Payabli's embedded components in a React application.

### Step 1: Create the hook

Create a hook that allows you to use the embedded component and execute its methods.
The hook needs to inject the Payabli library script and initialize the embedded component with the provided configuration.
Make a new file for the `usePayabli` hook and add the following code:

```ts TypeScript
// usePayabli.ts
import { useState, useEffect, useRef, useCallback } from "react";

const useScript = (src: string) => {
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    const existingScript = document.querySelector(`script[src="${src}"]`);

    const onLoad = () => {
      setIsLoaded(true);
    };

    if (!existingScript) {
      const script = document.createElement("script");
      script.src = src;
      script.async = true;

    script.addEventListener("load", onLoad);

    document.body.appendChild(script);
    
    return () => {
      script.removeEventListener("load", onLoad);
      document.body.removeChild(script);
    };
        } else {
    if (existingScript.getAttribute("data-loaded") === "true") {
      setIsLoaded(true);
    } else {
      existingScript.addEventListener("load", onLoad);
    }
        }
  }, [src]);

  return isLoaded;
};

declare var PayabliComponent: any;

export const usePayabli = (
  options: any,
  method: string,
  parameters: any = null,
  production: boolean = false
) => {
  const [payOptions, setPayOptions] = useState(options);
  const [isInitialized, setIsInitialized] = useState(false);
  const payComponentRef = useRef<any>(null);
  const initCallbacks = useRef<(() => void)[]>([]); // Queue for functions waiting on initialization

  const scriptSrc = production ? "https://embedded-component.payabli.com/component.js" : "https://embedded-component-sandbox.payabli.com/component.js";
  
  const isScriptLoaded = useScript(scriptSrc);

  useEffect(() => {
    if (isScriptLoaded) {
      payComponentRef.current = new PayabliComponent(payOptions);
      setIsInitialized(true);
  
      // payabliExecute queued callbacks
      initCallbacks.current.forEach((cb) => cb());
      initCallbacks.current = []; // Clear the queue
    }
  }, [isScriptLoaded, payOptions]);

  useEffect(() => {
    if (isInitialized && payComponentRef.current) {
      payComponentRef.current.updateConfig(payOptions);
    }
  }, [isInitialized, payOptions]);

  const payabliReinit = useCallback(() => {
    if (isInitialized && payComponentRef.current) {
      payComponentRef.current.payabliExec("reinit");
    }
  }, [isInitialized]);

  const payabliExec = useCallback(() => {
    const payabliExecuteMethod = () => {
      if (parameters != null) {
        payComponentRef.current.payabliExec(method, parameters);
      } else {
        payComponentRef.current.payabliExec(method);
      }
    };

    if (isInitialized && payComponentRef.current) {
      payabliExecuteMethod();
    } else {
      initCallbacks.current.push(payabliExecuteMethod); // Queue the payabliExecution
    }
  }, [isInitialized, method, parameters]);

  return [payOptions, setPayOptions, payabliExec, payabliReinit];
};
```

```js JavaScript
// usePayabli.js
import { useState, useEffect, useRef, useCallback } from "react";

const useScript = (src) => {
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    const existingScript = document.querySelector(`script[src="${src}"]`);

    const onLoad = () => {
      setIsLoaded(true);
    };

    if (!existingScript) {
      const script = document.createElement("script");
      script.src = src;
      script.async = true;

      script.addEventListener("load", onLoad);

      document.body.appendChild(script);

      return () => {
        script.removeEventListener("load", onLoad);
        document.body.removeChild(script);
      };
    } else {
      if (existingScript.getAttribute("data-loaded") === "true") {
        setIsLoaded(true);
      } else {
        existingScript.addEventListener("load", onLoad);
      }
    }
  }, [src]);

  return isLoaded;
};

export const usePayabli = (
  options,
  method,
  parameters = null,
  production = false
) => {
  const [payOptions, setPayOptions] = useState(options);
  const [isInitialized, setIsInitialized] = useState(false);
  const payComponentRef = useRef(null);
  const initCallbacks = useRef([]); // Queue for functions waiting on initialization

  const scriptSrc = production ? "https://embedded-component.payabli.com/component.js" : "https://embedded-component-sandbox.payabli.com/component.js";
  
  const isScriptLoaded = useScript(scriptSrc);

  useEffect(() => {
    if (isScriptLoaded) {
      payComponentRef.current = new PayabliComponent(payOptions);
      setIsInitialized(true);
  
      // Execute queued callbacks
      initCallbacks.current.forEach((cb) => cb());
      initCallbacks.current = []; // Clear the queue
    }
  }, [isScriptLoaded, payOptions]);

  useEffect(() => {
    if (isInitialized && payComponentRef.current) {
      payComponentRef.current.updateConfig(payOptions);
    }
  }, [isInitialized, payOptions]);

  const payabliReinit = useCallback(() => {
    if (isInitialized && payComponentRef.current) {
      payComponentRef.current.payabliExec("reinit");
    }
  }, [isInitialized]);

  const payabliExec = useCallback(() => {
    const payabliExecuteMethod = () => {
      if (parameters != null) {
        payComponentRef.current[method](parameters);
      } else {
        payComponentRef.current[method]();
      }
    };

    if (isInitialized && payComponentRef.current) {
      payabliExecuteMethod();
    } else {
      initCallbacks.current.push(payabliExecuteMethod); // Queue the execution
    }
  }, [isInitialized, method, parameters]);

  return [payOptions, setPayOptions, payabliExec, payabliReinit];
};
```

### Step 2: Create the component

Create a new `PayabliCheckout` component that passes in the configuration object for the embedded component to the `usePayabli` hook.
The `PayabliCheckout` component uses the `payabliExec` function to execute the embedded component's method.
Create a new file in the same directory and add the following code:

There are multiple types of embedded components with different use cases.
See the [Embedded Components Overview](/guides/pay-in-components-overview#choose-a-component) to decide which component type is best for you.

```tsx TSX
// PayabliCheckout.tsx
import { usePayabli } from './usePayabli';

export const PayabliCheckout = () => {
  const token = "o.z8j8aaztW9tUtUg4d.."
  const entryPoint = "bozeman-aikido"
  const rootContainer = "pay-component-1"
  const payabliButton = "btnx"

  const [payabliConfig, setPayabliConfig, payabliExec] = usePayabli({
    type: "methodEmbedded",
    rootContainer: rootContainer,
    defaultOpen: 'card',  // offering only Card method - Embedded UI can only show a payment method
    // customCssUrl: "your url to a custom css file",
    token: token,
    entryPoint: entryPoint,
    card: {
        enabled: true,
        amex: true,
        discover: true,
        visa: true,
        mastercard: true,
        jcb: true,
        diners: true,
        inputs: {   // here we are customizing the input fields
            cardHolderName: {
                label: "NAME ON CARD",
                placeholder: "",
                floating: false,
                value: "John Doe",
                size: 12,
                row: 0,
                order: 0
            },
            cardNumber: {
                label: "CARD NUMBER",
                placeholder: "1234 1234 1234 1234",
                floating: false,
                size: 6,
                row: 1,
                order: 0
            },
            cardExpirationDate: {
                label: "EXPIRATION DATE",
                placeholder: "MM/YY",
                floating: false,
                size: 6,
                row: 1,
                order: 1
            },
            cardCvv: {
                label: "CVV/CVC",
                placeholder: "CVV/CVC",
                floating: false,
                size: 6,
                row: 2,
                order: 0,
            },
            cardZipcode: {
                label: "ZIP/POSTAL CODE",
                placeholder: "ZIP/POSTAL CODE",
                floating: false,
                size: 6,
                row: 2,
                order: 1,
                country: ["us", "ca"],
            }
        }
    },
    ach: {
        enabled: false,
        checking: true,
        savings: true
    },
    customerData: {
        customerNumber: "00001",
        firstName: "John",
        lastName: "Doe",
        billingEmail: "johndoe@email.com"
    },
    functionCallBackSuccess: (response: any) => {
      const containerEl = document.getElementById(rootContainer);
      const responseText = JSON.stringify(response.responseText);
      const responseData = JSON.stringify(response.responseData);
      alert(responseText + " " + responseData);
      containerEl!.innerHTML += `
        <hr/>
        <p><b>Embedded Component Response:</b></p>
        <p>${responseText}</p>
        <p>${responseData}</p>
        <hr/>
      `;
    },
    functionCallBackReady: (data: any) => {
        var btn = document.getElementById(payabliButton);
        if (data[1] === true) {
            btn!.classList.remove("hidden");
        } else {
            if (!btn!.classList.contains("hidden")) {
                btn!.classList.add("hidden");
            }
        }
    },
    functionCallBackError: (errors: any) => {
        alert('Error!');
        console.log(errors);
    }
  },
  "pay", {
    paymentDetails: {
      totalAmount: 100,
      serviceFee: 0,
      categories: [
        {
          label: "payment",
          amount: 100,
          qty: 1,
        },
      ],
    },
  })

  return (
    <div>
      <div id="pay-component-1"></div>
      <button id="btnx" className="hidden" onClick={payabliExec}>Pay</button>
      <button onClick={() => setPayabliConfig({ 
        ...payabliConfig, 
        card: { 
          ...payabliConfig.card, 
          inputs: { 
            ...payabliConfig.card.inputs, 
            cardHolderName: { 
              ...payabliConfig.card.inputs.cardHolderName, 
              value: "Johnny Dover" 
            }
          } 
        }
      })}>Switch to Johnny Dover</button>
    </div>
  )
}
```

```jsx JSX
// PayabliCheckout.jsx
import { usePayabli } from './usePayabli';

export const PayabliCheckout = () => {
  const token = "o.z8j8aaztW9tUtUg4d.."
  const entryPoint = "bozeman-aikido"
  const rootContainer = "pay-component-1"
  const payabliButton = "btnx"

  const [payabliConfig, setPayabliConfig, payabliExec] = usePayabli({
    type: "methodEmbedded",
    rootContainer: rootContainer,
    defaultOpen: 'card',  // offering only Card method - Embedded UI can only show a payment method
    // customCssUrl: "your url to a custom css file",
    token: token,
    entryPoint: entryPoint,
    card: {
        enabled: true,
        amex: true,
        discover: true,
        visa: true,
        mastercard: true,
        jcb: true,
        diners: true,
        inputs: {   // here we are customizing the input fields
            cardHolderName: {
                label: "NAME ON CARD",
                placeholder: "",
                floating: false,
                value: "John Doe",
                size: 12,
                row: 0,
                order: 0
            },
            cardNumber: {
                label: "CARD NUMBER",
                placeholder: "1234 1234 1234 1234",
                floating: false,
                size: 6,
                row: 1,
                order: 0
            },
            cardExpirationDate: {
                label: "EXPIRATION DATE",
                placeholder: "MM/YY",
                floating: false,
                size: 6,
                row: 1,
                order: 1
            },
            cardCvv: {
                label: "CVV/CVC",
                placeholder: "CVV/CVC",
                floating: false,
                size: 6,
                row: 2,
                order: 0,
            },
            cardZipcode: {
                label: "ZIP/POSTAL CODE",
                placeholder: "ZIP/POSTAL CODE",
                floating: false,
                size: 6,
                row: 2,
                order: 1,
                country: ["us", "ca"],
            }
        }
    },
    ach: {
        enabled: false,
        checking: true,
        savings: true
    },
    customerData: {
        customerNumber: "00001",
        firstName: "John",
        lastName: "Doe",
        billingEmail: "johndoe@email.com"
    },
    functionCallBackSuccess: (response) => {
      const containerEl = document.getElementById(rootContainer);
      const responseText = JSON.stringify(response.responseText);
      const responseData = JSON.stringify(response.responseData);
      alert(responseText + " " + responseData);
      containerEl.innerHTML += `
        <hr/>
        <p><b>Embedded Component Response:</b></p>
        <p>${responseText}</p>
        <p>${responseData}</p>
        <hr/>
      `;
    },
    functionCallBackReady: (data) => {
        var btn = document.getElementById(payabliButton);
        if (data[1] === true) {
            btn.classList.remove("hidden");
        } else {
            if (!btn.classList.contains("hidden")) {
                btn.classList.add("hidden");
            }
        }
    },
    functionCallBackError: (errors) => {
        alert('Error!');
        console.log(errors);
    }
  },
  "pay", {
    paymentDetails: {
      totalAmount: 100,
      serviceFee: 0,
      categories: [
        {
          label: "payment",
          amount: 100,
          qty: 1,
        },
      ],
    },
  })

  return (
    <div>
      <div id="pay-component-1"></div>
      <button id="btnx" className="hidden" onClick={payabliExec}>Pay</button>
      <button onClick={() => setPayabliConfig({ 
        ...payabliConfig, 
        card: { 
          ...payabliConfig.card, 
          inputs: { 
            ...payabliConfig.card.inputs, 
            cardHolderName: { 
              ...payabliConfig.card.inputs.cardHolderName, 
              value: "Johnny Dover" 
            }
          } 
        }
      })}>Switch to Johnny Dover</button>
    </div>
  )
}
```

```tsx TSX
// PayabliCheckout.tsx
import { usePayabli } from './usePayabli.ts';

export const PayabliCheckout = () => {
  const token = "o.z8j8aaztW9tUtUg4d.."
  const entryPoint = "bozeman-aikido"

  const [payabliConfig, setPayabliConfig, payabliExec, payabliReinit] = usePayabli({
  type: "methodLightbox",
  rootContainer: "pay-component-1",
  buttonLabelInModal: 'Save Payment Method',
  defaultOpen: 'ach',
  hideComponent: true,
  token: token,
  entryPoint: entryPoint,
  card: {
      enabled: true,
      amex: true,
      discover: true,
      visa: true,
      mastercard: true,
      jcb: true,
      diners: true
  },
  ach: {
      enabled: true,
      checking: true,
      savings: false
  },
  customerData: {
      customerNumber: "00001",
      firstName: "John",
      lastName: "Doe",
      billingEmail: "johndoe@email.com"
  },

  functionCallBackSuccess: (response: any) => {
    // This callback covers both 2XX and 4XX responses
    console.log(response);
    switch (response.responseText) {
      case "Success":
        // Tokenization was successful
        alert(`Success: ${response.responseData.resultText}`);
        break;
      case "Declined":
        // Tokenization failed due to processor decline or validation errors
        // Recommend reinitialization of the component so that the user can try again
        // with different card data
        alert(`Declined: ${response.responseData.resultText}`);
        payabliReinit()
        break;
      default:
        // Other response text. These are normally errors with Payabli internal validations
        // before processor engagement
        // We recommend reinitializing the component.
        // If the problem persists, contact Payabli to help debug
        alert(`Error: ${response.responseText}`);
        payabliReinit()
        break;
    }
  },

  functionCallBackError: (errors: any) => {
        // This callback covers 5XX response or parsing errors
        // We recommend reinitializing the component.
        // If the problem persists, contact Payabli to help debug
    console.log(errors);
    payabliReinit()
  }
  },
  "pay", {
    paymentDetails: {
      totalAmount: 100,
      serviceFee: 0,
      categories: [
        {
          label: "payment",
          amount: 100,
          qty: 1,
        },
      ],
    },
  })

  return (
    <div>
      <div id="pay-component-1"></div>
      <button onClick={payabliExec}>Pay</button>
      <button onClick={() => setPayabliConfig({ 
        ...payabliConfig, 
        card: { 
          ...payabliConfig.card, 
          enabled: !payabliConfig.card.enabled 
        }
      })}>Toggle Card Payments</button>
    </div>
  )
}
```

```jsx JSX
// PayabliCheckout.jsx
import { usePayabli } from './usePayabli.js';

export const PayabliCheckout = () => {
  const token = "o.z8j8aaztW9tUtUg4d.."
  const entryPoint = "bozeman-aikido"

  const [payabliConfig, setPayabliConfig, payabliExec, payabliReinit] = usePayabli({
  type: "methodLightbox",
  rootContainer: "pay-component-1",
  buttonLabelInModal: 'Save Payment Method',
  defaultOpen: 'ach',
  hideComponent: true,
  token: token,
  entryPoint: entryPoint,
  card: {
      enabled: true,
      amex: true,
      discover: true,
      visa: true,
      mastercard: true,
      jcb: true,
      diners: true
  },
  ach: {
      enabled: true,
      checking: true,
      savings: false
  },
  customerData: {
      customerNumber: "00001",
      firstName: "John",
      lastName: "Doe",
      billingEmail: "johndoe@email.com"
  },

  functionCallBackSuccess: (response) => {
    // This callback covers both 2XX and 4XX responses
    console.log(response);
    switch (response.responseText) {
      case "Success":
        // Tokenization was successful
        alert(`Success: ${response.responseData.resultText}`);
        break;
      case "Declined":
        // Tokenization failed due to processor decline or validation errors
        // Recommend reinitialization of the component so that the user can try again
        // with different card data
        alert(`Declined: ${response.responseData.resultText}`);
        payabliReinit()
        break;
      default:
        // Other response text. These are normally errors with Payabli internal validations
        // before processor engagement
        // We recommend reinitializing the component.
        // If the problem persists, contact Payabli to help debug
        alert(`Error: ${response.responseText}`);
        payabliReinit()
        break;
    }
  },

  functionCallBackError: (errors) => {
        // This callback covers 5XX response or parsing errors
        // We recommend reinitializing the component.
        // If the problem persists, contact Payabli to help debug
    console.log(errors);
    payabliReinit()
  }
  },
  "pay", {
    paymentDetails: {
      totalAmount: 100,
      serviceFee: 0,
      categories: [
        {
          label: "payment",
          amount: 100,
          qty: 1,
        },
      ],
    },
  })

  return (
    <div>
      <div id="pay-component-1"></div>
      <button onClick={payabliExec}>Pay</button>
      <button onClick={() => setPayabliConfig({ 
        ...payabliConfig, 
        card: { 
          ...payabliConfig.card, 
          enabled: !payabliConfig.card.enabled 
        }
      })}>Toggle Card Payments</button>
    </div>
  )
}
```

### Types

The hook receives the following arguments:

The configuration object for the embedded component. See the [configuration reference](/guides/pay-in-components-embeddedmethod-ui#configuration-reference) for the component type you are using.

The method to execute in `payabliExec`. See the [field](/guides/pay-in-components-overview#param-payabli-exec-action-parameters) for more information.

An optional object that contains objects to pass to the `method`. See the [`paymentMethod`](/guides/pay-in-components-embeddedmethod-ui#param-payment-method), [`paymentDetails`](/guides/pay-in-components-embeddedmethod-ui#param-payment-details), or [`customerData`](/guides/pay-in-components-embeddedmethod-ui#param-customer-data) objects for more information.

A boolean value that determines whether to use the production or sandbox environment.

The hook returns an array with the following elements:

The configuration object for the embedded component. See the [configuration reference](/guides/pay-in-components-embeddedmethod-ui#configuration-reference) for the component type you are using.

A function to update the configuration object for the embedded component. This allows you to dynamically change the options after initialization.

A function to execute the embedded component's method. This will call the method specified in the `method` argument passed to the hook.

A function to reinitialize the embedded component.

## Vue

Visit the [Vue integration example](https://github.com/payabli/examples/tree/main/vue-integration) to see Payabli's embedded components in a Vue application.

### Step 1: Create the composable

Create a composable that allows you to use the embedded component and execute its methods.
The composable needs to inject the Payabli library script and initialize the embedded component with the provided configuration.
Make a new file for the `usePayabli` composable and add the following code:

```ts TypeScript
// usePayabli.ts
import { ref, reactive, onMounted, watchEffect } from 'vue';

const loadedScripts = new Set<string>();

const useScript = (src: string) => {
  const isLoaded = ref(false);

  onMounted(() => {
    if (loadedScripts.has(src)) {
      isLoaded.value = true;
      return;
    }

    const existingScript = document.querySelector(`script[src="${src}"]`);

    const handleLoad = () => {
      loadedScripts.add(src);
      isLoaded.value = true;
      script.setAttribute("data-loaded", "true");
    };

    let script: HTMLScriptElement;

    if (!existingScript) {
      script = document.createElement("script");
      script.src = src;
      script.async = true;
      script.addEventListener("load", handleLoad);
      document.body.appendChild(script);
    } else {
      if (existingScript.getAttribute("data-loaded") === "true") {
        isLoaded.value = true;
      } else {
        existingScript.addEventListener("load", handleLoad);
      }
    }
  });

  return isLoaded;
};

declare var PayabliComponent: any;

export const usePayabli = (
  options: any,
  method: string,
  parameters: any = null,
  production = false
) => {
  const payOptions = reactive({ ...options });
  const isInitialized = ref(false);
  const payComponentRef = ref<any>(null);
  const initCallbacks: (() => void)[] = [];

  const scriptSrc = production
    ? "https://embedded-component.payabli.com/component.js"
    : "https://embedded-component-sandbox.payabli.com/component.js";

  const isScriptLoaded = useScript(scriptSrc);

  const initPayabli = () => {
    if (!isScriptLoaded.value || isInitialized.value) return;

    payComponentRef.value = new PayabliComponent(payOptions);
    isInitialized.value = true;

    initCallbacks.splice(0).forEach(cb => cb());
  };

  watchEffect(() => {
    if (isScriptLoaded.value) {
      initPayabli();
    }
  });

  watchEffect(() => {
    if (isInitialized.value && payComponentRef.value) {
      payComponentRef.value.updateConfig(payOptions);
    }
  });

  const payabliReinit = () => {
    if (isInitialized.value && payComponentRef.value) {
      payComponentRef.value.payabliExec("reinit");
    }
  };

  const payabliExec = () => {
    const exec = () => {
      if (!payComponentRef.value) return;

      if (payOptions.type === "methodEmbedded") {
        if (parameters != null) {
          payComponentRef.value.payabliExec(method, parameters);
        } else {
          payComponentRef.value.payabliExec(method);
        }
      } else if (
        payOptions.type === "methodLightbox" ||
        payOptions.type === "vterminal"
      ) {
        payComponentRef.value.showModal();
      }
    };

    if (isInitialized.value) {
      exec();
    } else {
      initCallbacks.push(exec);
    }
  };

  return [payOptions, payabliExec, payabliReinit] as const;
};
```

### Step 2: Create the component

Create a new `PayabliCheckout` component that passes in the configuration object for the embedded component to the `usePayabli` composable.
The `PayabliCheckout` component uses the `payabliExec` function to execute the embedded component's method.
Create a new file in the same directory and add the following code:

```vue Vue

<template>
  <div>
    <div id="pay-component-1"></div>
    <button id="btnx" class="hidden" @click="payabliExec">Pay</button>
    <button @click.prevent="switchToJohnnyDover">Switch to Johnny Dover</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { usePayabli } from '../composables/usePayabli.ts';

const token = "o.z8j8aaztW9tUtUg4d..";
const entryPoint = "bozeman-aikido";
const rootContainer = "pay-component-1";
const payabliButton = "btnx";

const [payabliConfig, payabliExec] = usePayabli({
  type: "methodEmbedded",
  rootContainer: rootContainer,
  defaultOpen: 'card',
  token: token,
  entryPoint: entryPoint,
  card: {
    enabled: true,
    amex: true,
    discover: true,
    visa: true,
    mastercard: true,
    jcb: true,
    diners: true,
    inputs: {
      cardHolderName: {
        label: "NAME ON CARD",
        placeholder: "",
        floating: false,
        value: "John Doe",
        size: 12,
        row: 0,
        order: 0
      },
      cardNumber: {
        label: "CARD NUMBER",
        placeholder: "1234 1234 1234 1234",
        floating: false,
        size: 6,
        row: 1,
        order: 0
      },
      cardExpirationDate: {
        label: "EXPIRATION DATE",
        placeholder: "MM/YY",
        floating: false,
        size: 6,
        row: 1,
        order: 1
      },
      cardCvv: {
        label: "CVV/CVC",
        placeholder: "CVV/CVC",
        floating: false,
        size: 6,
        row: 2,
        order: 0,
      },
      cardZipcode: {
        label: "ZIP/POSTAL CODE",
        placeholder: "ZIP/POSTAL CODE",
        floating: false,
        size: 6,
        row: 2,
        order: 1,
        country: ["us", "ca"],
      }
    }
  },
  ach: {
    enabled: false,
    checking: true,
    savings: true
  },
  customerData: {
    customerNumber: "00001",
    firstName: "John",
    lastName: "Doe",
    billingEmail: "johndoe@email.com"
  },
  functionCallBackSuccess: (response) => {
    const containerEl = document.getElementById(rootContainer);
    const responseText = JSON.stringify(response.responseText);
    const responseData = JSON.stringify(response.responseData);
    alert(responseText + " " + responseData);
    containerEl.innerHTML += `
      <hr/>
      <p><b>Embedded Component Response:</b></p>
      <p>${responseText}</p>
      <p>${responseData}</p>
      <hr/>
    `;
  },
  functionCallBackReady: (data) => {
    const btn = document.getElementById(payabliButton);
    if (data[1] === true && btn) {
      btn.classList.remove("hidden");
    } else if (btn) {
      btn.classList.add("hidden");
    }
  },
  functionCallBackError: (errors) => {
    alert('Error!');
    console.log(errors);
  }
},
"pay", {
  paymentDetails: {
    totalAmount: 100,
    serviceFee: 0,
    categories: [
      {
        label: "payment",
        amount: 100,
        qty: 1,
      },
    ],
  },
});

const switchToJohnnyDover = () => {
  payabliConfig.card.inputs.cardHolderName.value = "Johnny Dover";
};
</script>

<style scoped>
.hidden {
  display: none;
}
</style>
```

### Types

The composable receives the following arguments:

The configuration object for the embedded component. See the [configuration reference](/guides/pay-in-components-embeddedmethod-ui#configuration-reference) for the component type you are using.

The method to execute in `payabliExec`. See the [field](/guides/pay-in-components-overview#param-payabli-exec-action-parameters) for more information.

An optional object that contains objects to pass to the `method`. See the [`paymentMethod`](/guides/pay-in-components-embeddedmethod-ui#param-payment-method), [`paymentDetails`](/guides/pay-in-components-embeddedmethod-ui#param-payment-details), or [`customerData`](/guides/pay-in-components-embeddedmethod-ui#param-customer-data) objects for more information.

A boolean value that determines whether to use the production or sandbox environment.

The composable returns an array with the following elements:

The configuration object for the embedded component. See the [configuration reference](/guides/pay-in-components-embeddedmethod-ui#configuration-reference) for the component type you are using.

A function to execute the embedded component's method. This will call the method specified in the `method` argument passed to the hook.

A function to reinitialize the embedded component.

## React Native

Payabli offers a React Native example app built with Expo that demonstrates how to render and use Payabli's embedded components in a React Native environment.
This section covers the file structure of the React Native example app and how to run it locally.
Visit the [React Native integration example](https://github.com/payabli/examples/tree/main/react-native) to see the source code on GitHub.

### File structure

To use Payabli's embedded components in a React Native application, you must render them inside of a `<WebView>` component.
The React Native example app contains utility files to render the embedded component inside of a `<WebView>`, and demo files that show how to use the utility files.

#### Utility files

The React Native example contains utility files that help to render the embedded component inside of a `<WebView>` and communicate between the React Native app and the embedded component.

This file contains the configuration object for the embedded component.
To customize fields and payment methods, update the configuration in this file.
For more information on the configuration object and its fields, see the [configuration reference](/guides/pay-in-components-embeddedmethod-ui#configuration-reference) for the component type you are using.

This file contains functions that receive messages from the embedded component in the React Native app.
It also defines the shape of messages received from the embedded component.

This file initializes the embedded component.
It sets up the message bridge in the embedded component's `<WebView>` so it can send messages to the React Native app.
It works with `bridge.ts` to support communication between the embedded component and the app.

This file contains the TypeScript types used in the React Native example.
These types include customer data shapes and payment method values.

This hook composes the utility files into reusable embedded component logic.
It initializes the component and wires up the WebView integration.

This component renders Payabli's embedded component inside a React Native `<WebView>`.
It uses the `usePayabliWebView` hook to initialize the component and handle app communication.

#### Demo files

The React Native example contains demo files that show how to use the previous utility files to render the embedded component and handle its callbacks.
These files don't contain core logic to render Payabli's embedded components or process payments.
You can remove these files without affecting the functionality of the embedded component in your app.

This demo component renders Payabli's embedded component with `PayabliEmbeddedWebView`.
The `PayabliCheckout` component is a screen with three stages:

The user can review their order details and edit transaction details.
The order details and transaction data are passed into the embedded component's setup functionality.

<img width="200px" src="https://files.buildwithfern.com/payabli.docs.buildwithfern.com/3cd881121c91caffc958e8b0247906131cea4cb96b3c5c101be885298775e9fa/images/react-native-review-screen.png" alt="review page of React Native example app" />

The Payabli embedded component is rendered inside a `<WebView>`.
The user can enter their payment information and submit the transaction.

<img width="200px" src="https://files.buildwithfern.com/payabli.docs.buildwithfern.com/a3f8f9dd722aa15eef757456335cdf5940ab8b1645b04fa33090a58e60613029/images/react-native-payment-screen.png" alt="payment page of React Native example app" />

The Payabli embedded component sends a message to the React Native app with the transaction result.
The user sees the result of their transaction.

<img width="200px" src="https://files.buildwithfern.com/payabli.docs.buildwithfern.com/fb52237bac43ad79262092b30f5e55c0236bebfbda5d913340dd2cc375c7bcb3/images/react-native-result-screen.png" alt="result page of React Native example app" />

This demo component is the home page of the React Native example app.
Click *Checkout* to see Payabli's embedded component in action.

This demo hook manages the checkout state in the `PayabliCheckout` component.
It manages the order details, transaction data, and current stage of the checkout flow.

#### File reference

See the table for a reference of the file structure of the React Native example app along with links to source code:

| File                         | Path           | Source                                                                                                          |
| ---------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- |
| `config.ts`                  | `lib/payabli/` | [View source](https://github.com/payabli/examples/blob/main/react-native/lib/payabli/config.ts)                 |
| `bridge.ts`                  | `lib/payabli/` | [View source](https://github.com/payabli/examples/blob/main/react-native/lib/payabli/bridge.ts)                 |
| `bootstrap.ts`               | `lib/payabli/` | [View source](https://github.com/payabli/examples/blob/main/react-native/lib/payabli/bootstrap.ts)              |
| `model.ts`                   | `lib/payabli/` | [View source](https://github.com/payabli/examples/blob/main/react-native/lib/payabli/model.ts)                  |
| `usePayabliWebView.ts`       | `hooks/`       | [View source](https://github.com/payabli/examples/blob/main/react-native/hooks/usePayabliWebView.ts)            |
| `PayabliEmbeddedWebView.tsx` | `components/`  | [View source](https://github.com/payabli/examples/blob/main/react-native/components/PayabliEmbeddedWebView.tsx) |
| `PayabliCheckout.tsx`        | `components/`  | [View source](https://github.com/payabli/examples/blob/main/react-native/components/PayabliCheckout.tsx)        |
| `HomePage.tsx`               | `components/`  | [View source](https://github.com/payabli/examples/blob/main/react-native/components/HomePage.tsx)               |
| `useCheckoutDemo.ts`         | `hooks/`       | [View source](https://github.com/payabli/examples/blob/main/react-native/hooks/useCheckoutDemo.ts)              |

### Run the app locally

This section covers how to run the React Native example app locally on your machine.

#### Dependencies

Before you begin, make sure you have the following installed on your machine:

* [git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/)

To run the app in your development environment, you need to set up an iOS or Android emulator.
See the [Expo docs](https://docs.expo.dev/get-started/set-up-your-environment/) for instructions on how to set up your environment for React Native development.

To run the app on a phone, see the [Expo docs](https://docs.expo.dev/get-started/installation/) for instructions on how to set up Expo Go on your device.

#### Set up the app

To run the React Native example app locally, follow these steps:

Clone the [Payabli examples repository](https://github.com/payabli/examples) to your local machine:

```bash
git clone https://github.com/payabli/examples
```

Change into the React Native example directory:

```bash
cd examples/react-native
```

Install the dependencies for the React Native example app:

```bash
npm install
```

Copy the environment variable template file:

```bash
cp .env.example .env
```

Open the `.env` file and fill in the required environment variables:

```txt
PAYABLI_API_KEY=your-api-key-here
PAYABLI_ENTRY_POINT=your-entry-point-here
```

Start the React Native example app on your desired platform and environment:

```bash
npm run ios
```

```bash
npm run android
```

```bash
npm start
```

This command starts the Expo development server and displays a QR code in your terminal.
Scan the QR code to open the app in Expo Go on your phone.

You should now see the app running in your emulator, and you can navigate to the checkout screen to see Payabli's embedded component in action.

You just set up the React Native example app to run locally on your machine!
Use this example app as a reference for your own React Native projects.

## Related resources

See these related resources to help you get the most out of Payabli.

* **[Embedded components overview](/guides/pay-in-components-overview)** - Learn how to use Payabli's embedded components to create customized checkout experiences without handling sensitive payment information yourself
* **[EmbeddedMethod UI](/guides/pay-in-components-embeddedmethod-ui)** - Learn how to use the EmbeddedMethod UI embedded component to add the ability to securely store a payment profile or execute a sale