---
title: "Framework Integration"
url: "https://developer.incode.com/sdk-reference/web-sdk-2-framework-integration/"
section: "sdk-reference"
group: "Incode Web SDK 2 Reference / Web SDK 2 Getting Started"
version: "v1.1"
status: "live"
---
# Framework Integration

:::note
This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation [here](/sdk-reference/web-sdk-reference).  Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade. <br /><br />Full rollout to all clients still TBD.
:::

Every Incode UI module ships as a [standard Web Component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) — registered via a side-effect import (`import '@incodetech/web/<module>'`) and consumed like any HTML element. Because the elements are framework-agnostic, the same SDK code runs from vanilla HTML, React, Angular, Vue, or any other web framework. The only difference is how each framework lets you mount custom elements and bind non-string properties.

This page documents the four mainstream paths. All of them share one rule:

> **Set `config`, `onFinish`, and `onError` as JavaScript properties on the element**, not as HTML attributes. They are object/function values, not strings, so they can't go through `setAttribute`. Each framework section below shows how to do that idiomatically.

## Vanilla HTML / TypeScript

```html
<incode-flow id="flow"></incode-flow>

<script type="module">
  import { setup } from '@incodetech/core';
  import '@incodetech/web/flow';
  import '@incodetech/web/flow/styles.css';

  await setup({
    apiURL: 'https://demo-api.incodesmile.com',
    token: session.token,
  });

  const flow = document.getElementById('flow');
  flow.config = { token: session.token };
  flow.onFinish = (result) => {
    /* ... */
  };
</script>
```

The same shape applies to every other module — replace `flow` with `phone`, `selfie`, `id`, etc., and set the corresponding config / handlers on the element.

## React

### Universal pattern (works on React 16.8+)

Use the custom element directly with a ref. React renders custom elements as-is, and you assign non-string properties imperatively in `useEffect`. This works on every React version since hooks landed:

```tsx
import { useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import type { FlowConfig } from '@incodetech/web/flow';
import type { FinishStatus } from '@incodetech/core/flow';
import '@incodetech/web/flow';
import '@incodetech/web/flow/styles.css';

type FlowElement = HTMLElement & {
  config: FlowConfig;
  onFinish: (result?: FinishStatus) => void;
};

export function MyFlow({ token }: { token: string }) {
  const ref = useRef<FlowElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.config = { token };
    el.onFinish = (result) => console.log('Done:', result);
  }, [token]);

  return (
    <incode-flow ref={ref} style={{ display: 'block', height: '100vh' }} />
  );
}
```

### React 19+ shortcut

React 19 ships native custom-element support: it sets non-string JSX props as DOM properties and bridges `on*` props through `addEventListener`. If you're on React 19 you can drop the ref + `useEffect` ceremony entirely:

```tsx
import { setup } from '@incodetech/core';
import '@incodetech/web/flow';
import '@incodetech/web/flow/styles.css';

export function MyFlow({ token }: { token: string }) {
  return (
    <incode-flow
      config={{ token }}
      onFinish={(result) => console.log('Done:', result)}
      onError={(error, code) => console.error(error, code)}
      style={{ display: 'block', height: '100vh' }}
    />
  );
}
```

This shortcut **does not work on React 18 or earlier** — those versions serialize JSX props through `setAttribute`, which coerces `config={{token}}` to the string `"[object Object]"`. Use the universal pattern above unless you're certain every consumer of your code is on React 19+.

### TypeScript: JSX support for `incode-*` tags

**React 19+** treats unknown lowercase tags as HTML elements automatically — no extra TypeScript setup is needed.

**React 18 and earlier** strict-checks `JSX.IntrinsicElements`, so referencing `<incode-flow>` (or any other Incode tag) directly in JSX errors out. Add a one-time augmentation somewhere in your project (e.g. `src/types/incode.d.ts`) that covers every `incode-*` tag your app uses:

```ts
// src/types/incode.d.ts
import 'react';

type IncodeElementAttrs = React.DetailedHTMLProps<
  React.HTMLAttributes<HTMLElement>,
  HTMLElement
>;

declare global {
  namespace JSX {
    interface IntrinsicElements {
      'incode-flow': IncodeElementAttrs;
      'incode-flow-completed': IncodeElementAttrs;
      'incode-selfie': IncodeElementAttrs;
      'incode-id': IncodeElementAttrs;
      'incode-phone': IncodeElementAttrs;
      'incode-email': IncodeElementAttrs;
      'incode-document-capture': IncodeElementAttrs;
      'incode-face-match': IncodeElementAttrs;
      'incode-consent': IncodeElementAttrs;
      'incode-geolocation': IncodeElementAttrs;
      'incode-antifraud': IncodeElementAttrs;
      'incode-curp-validation': IncodeElementAttrs;
      'incode-identity-reuse': IncodeElementAttrs;
      'incode-redirect-to-mobile': IncodeElementAttrs;
      'incode-signature': IncodeElementAttrs;
      'incode-electronic-signature': IncodeElementAttrs;
      'incode-ae-signature': IncodeElementAttrs;
      'incode-qe-signature': IncodeElementAttrs;
      'incode-ekyc': IncodeElementAttrs;
      'incode-ekyb': IncodeElementAttrs;
      'incode-workflow': IncodeElementAttrs;
      'incode-cross-document-data-match': IncodeElementAttrs;
      // Add any other incode-* tags you mount; the shape is identical.
    }
  }
}
```

The element-specific properties (`config`, `onFinish`, `onError`) are set imperatively through the ref — that's why `IncodeElementAttrs` is just the standard HTML element shape. The full per-tag catalog lives in [Web Components](/sdk-reference/web-sdk-2-web-components/).

## Angular

Add `CUSTOM_ELEMENTS_SCHEMA` to the module that uses Incode tags:

```typescript
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  declarations: [MyFlowComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
```

Then bind to the element's properties from a template using property binding:

```html
<incode-flow
  [config]="flowConfig"
  (onFinish)="handleFinish($event)"
></incode-flow>
```

## Vue 3

Tell the template compiler to treat `incode-*` tags as custom elements:

```typescript
// vite.config.ts
import vue from '@vitejs/plugin-vue';

vue({
  template: {
    compilerOptions: {
      isCustomElement: (tag) => tag.startsWith('incode-'),
    },
  },
});
```

Then bind properties with `.prop` modifiers:

```vue
<incode-flow :config.prop="flowConfig" :onFinish.prop="handleFinish" />
```

## See also

- [Getting Started](/sdk-reference/web-sdk-2-getting-started/): three integration paths walked through end to end
- [IncodeFlow Component](/sdk-reference/web-sdk-2-incodeflow-component/): the all-in-one component reference
- [Web Components](/sdk-reference/web-sdk-2-web-components/): full tag catalog
- [Individual Modules](/sdk-reference/web-sdk-2-individual-modules/): per-module reference pages
- [Troubleshooting → Build / TypeScript issues](/sdk-reference/web-sdk-2-troubleshooting/#build--typescript-issues): common JSX errors and fixes