How to render React components in Vue
Tags: vue, react, javascript, frontend
Published: 2024-04-06 22:50:51

You might be wondering, how is it possible to use React and Vue in the same application? Well, after all, React and Vue are two different libraries, but they are both JavaScript libraries. Components you write in JSX and Vue are compiled down to JavaScript render functions. So, technically, it is possible to combine JavaScript with JavaScript.

## Why?
Sometimes, we may want to create a UI library that can be integrated with any JavaScript UI library or framework. In such cases, we need to have a core that aligns with our favorite UI library or framework and create connectors for other UI libraries or frameworks so that our package can function seamlessly in any environment.

## Setup

1. Create a new Vue app with vite:
    ```bash
     # TypeScript
     npm create vite@latest my-vue-react-app -- --template vue-ts
     # JavaScript
     npm create vite@latest my-vue-react-app -- --template vue
    ```
2. Install `react`and and `react-dom` as dependencies :
    ```bash
     npm i react react-dom
    ```
3. Install `@vitejs/plugin-react` as devDependencies :
    ```bash
     npm install @vitejs/plugin-react --save-dev
    ```
4. Update `vite.config`:
    ```ts
     // ./vite.config.ts
     import { defineConfig } from 'vite'
     import vue from '@vitejs/plugin-vue'
     import react from '@vitejs/plugin-react'

     export default defineConfig({
       plugins: [vue(),react()],
     })
    ```

5. If you are using TypeScript, then install the necessary types for React and react-dom:
    ```bash
     npm install @types/react @types/react-dom --save-dev
    ```

## 1.Create a react Component

```tsx
// .scr/components/ReactComponent.tsx

import React from 'react'

export const ReactComponent:React.FC =()=>{
    return (
        <button>React Button</button>
    )
}
```

## 2.Create A Vanilla Connector
From a low-level perspective, a React component is simply a JavaScript function. Therefore, we are essentially passing a basic JavaScript function to react-dom.
So now what we can do is create a vanilla API, allowing our consumers to easily communicate with our React component.
```tsx
// ./scr/components/Connector.tsx
import { ReactComponent } from "./ReactComponent";
import { Root, createRoot } from 'react-dom/client';

export class ReactConnector {

    root:Root;

    constructor(targetEl:HTMLElement){
        this.root = createRoot(targetEl);
    }

    render(){
        this.root.render(<ReactComponent/>)
    }
}
```

## 3.Render react component in vue
Now all we need to do is create an instance of the ReactConnector and pass the target element in which we want our React component to be rendered. Whenever the target element mounts on the DOM, we can render our React component inside it.
Importing TSX and JSX in Vue won't cause any errors because we are simply importing JavaScript; there's nothing wrong with that.
```vue
<script setup lang="ts">
// ./scr/components/VueComponent.vue
import { onMounted } from 'vue'
import { ReactConnector } from './Connector';

onMounted(()=>{
 const reactComponent = new ReactConnector(document.getElementById('reactContainer')!)
 reactComponent.render()
})
</script>

<template>
  <div id="reactContainer">
      <!-- render react component in here -->
  </div>
  <div class="card">
    <button type="button">vue button</button>
  </div>
</template>
```
Now, if you run the app and check out the rendered `VueComponent.vue`, you can see that the React component is rendered inside it without any warnings or errors.
You can check out the final result in my GitHub repository:
[github.com/amirkian007/react-vue-mix](https://github.com/amirkian007/react-vue-mix)