Using React components directly in Vue components (with or without Typescript) — Part 2 — Full Tutorial

Cedric McKinnie
8 min readJan 9, 2019

In Part 1 — Overview, I gave an overview of my experience with integrating React and Vue into the same project (and file) including the motivation for this exploration and a breakdown of the tools involved in the process. I would recommend reading through that post for better context on how this integration works.

Full code example can be found here

Short Disclaimers:

I would highly suggest that you become familiar with Vue and React individually before experimenting with this concept in too much depth. Furthermore, it is always a good idea to do your homework before committing anything to a Production environment.

On a separate note, I provided a means for getting Typescript working in your Vue-React project but did not considering including all of the types in my examples given that using Typescript isn’t the focal point of this post. Please don’t be alarmed by the TSLint errors that you’ll receive when running this code. Comments below or pull requests to the sample repo with the improved typing, would be very much appreciated!

Vue Project Setup

I decided to use the new 🎉 Vue UI 🎉 to make things a little more interesting as well as to standardize the setup process to some extent.

npm install -g @vue/cli
vue ui

First, visit localhost:8080 (this port increments one unit at a time until it finds an available port)

Go ahead and create a new project wherever you please:

Give your project a name and select npm as your package manager:

Make sure to select the default preset which includes Babel:

Navigate to the “Plugins” tab and hit the “Add plugin” button in the top right corner:

Search for “typescript”:

Select the core @vue typescript implementation (please note the version in case of issues later in the process):

Proceed through the configuration table:

Navigate to the “tasks” tab, chose the “serve” task and hit the “Run task” button:

Open the project in your favorite javascript IDE. My personal preference is WebStorm but VSCode is another fantastic option.

Let’s clear everything from App.vue and make our own component so that we’re not just working with a simple “hello world” app. We’re now going to add some Vue components that use Typescript. Remember if you’re confused about anything in this tutorial, please refer to Part 1 of this series as I’ve provided most of the context there.

Add src/components/MyDashboard.tsx

Go ahead and import, register and drop your TSX component into your App.vue’s template like so

If you run the app at this point, you may encounter a ‘.babelrc not found’ error and may need to add a .babelrc file in your project root with an empty object in it to satisfy this error:

Your app should now be available at localhost:8080 (again, this port number is incremented by the server one unit at a time until an available port is found).

React Integration Setup

Simply showing a Vue component that says ‘My Dashboard’ is not particularly interesting so let’s proceed to use the React Material-UI library to make this a little more exciting.

First, we will of course need to crack open our terminal and add react and react-dom as dependencies:

npm install — save react react-dom

as well as the React Material-UI core dependency

npm install — save @material-ui/core

Now let’s make our React component to replace our title using the Material UI App Bar. The nice thing about Material-UI’s examples is that you can almost always copy the whole example as a starting point for a component that you’re building and it just works when you import it. Once the example is in your project, you can then apply your use case systematically to something that is already on the page. This is very helpful in learning how the whole library works. Make sure to include .react.tsx as your full file extension!

Now let’s pull this React component into our Vue dashboard component:

If you run the component now, you will run into an error. To resolve this, add Vuera to your project:

npm install — save Vuera

Then, import the Higher-Order Component (HOC) called ‘ReactInVue’ into your Vue dashboard component where you would like to use a React component so as to convert this React component into a Vue component:

const DashboardTitle = ReactInVue(DashboardTitleReact)

By this point, there is still one step left before this will work. Tell webpack to use the transform-react-jsx babel plugin and the @babel/preset-react preset to parse this as React JSX rather than Vue JSX. There is only a tiny difference between the two but as we all know, computers don’t really like to make guesses unless we program them to do so. So go ahead and add a vue.config.js file to your project root which will be used to override the default Webpack configurations set by vue-cli from earlier. Here are the contents of this file:

You’ll then need to add babel-plugin-transform-react-jsx and @babel/preset-react as dev-dependencies to your project.

npm install — save-dev @babel/preset-react babel-plugin-transform-react-jsx

Head on over to your application and you should see your new App Bar! You may need to npm install — save-dev @babel/plugin-proposal-export-default-from to handle an error about experimental Typescript element depending on your Typescript version.

You may also run into the following error:

URIError: Failed to decode param ‘/%3C%=%20BASE_URL%20%%3Efavicon.ico’

To resolve this one you’ll need to rename your public folder (I chose vpublic as in vue-public) and clone your favicon.ico into an empty public folder in your root. I don’t really like the idea of having to change the name of the public folder and I understand that a lot of people will have an aversion to doing so. I’m currently looking into better ways to do this but, if anyone has any recommendations for a better solution. Feel free to drop me a line! Your resulting project structure should look something like this:

Now rather than showing the default word “photos” from their example App Bar component, let’s pass in our title into our DashboardTitleReact component to take advantage of React props.

Then we can access this in our DashboardTitleReact component by retrieving the components props like so:

Voila!! You’ve successfully shared a property from a Vue parent component directly into a React child component.

Feature Demonstration

Here are a few examples that demonstrate how this integration captures a lot of the core capabilities of React.

Feature 1: Event handling (React and Vue)

You can bind an event to a React component embedded in your Vue app using the passedProps attribute. Despite the fact that you would normally bind an event handler directly to the component in React, this is nothing but a slight syntactic variation that happened because of the conversion from React component to Vue component.

Feature 2: Containment using children

You can embed a Vue child node inside a React component like below. In this case, we’re rendering on our Vue instance a React button with text from a Vue component.

A Vue instance rendering a React button with Vue Text

Feature 3: Refs

Refs appear to be operational however it is important to remember that Vue refs and React refs work differently internally. Vue uses string refs while React uses callback refs (react deprecated string refs a few versions ago). Both work in this context however you will have to use Vue refs when in a Vue component and React refs when in a React component. This should not cause an issue because the goal of using refs is, generally speaking, to be able to access a component’s DOM element directly (i.e. in order to focus an input or get the current size of an element).

Feature 4: Rendering multiple components (Using Array.map)

Feature 5: Component as prop for other component

Unfortunately, passing a component to the props of another component doesn’t work right off the bat but an issue has been opened on Github and is being addressed as a high priority. Fortunately, there is a work-around which isn’t bad at all. You’ll just need to make a component wrapper because otherwise babel will get confused which kind of JSX it’s looking at if you try to put React JSX inside of Vue JSX.

Limitations in Progress:

Forwarding Refs. Forwarding refs are a React-specific concept which is not currently usable with Vuera but an issue has been filed for it and was addressed as a high priority. For now, this StackOverflow post shows a solid workaround which is to just do it the way it was done before forwarding refs were included as part of the React arsenal:

Unfortunately, this limitation may cause issues if you try to load in an external component that takes advantage of this React feature. Personally, this issue isn’t too much of a deal breaker unless your existing application heavily relies on forwarding refs given that there are so many fantastic components out there that do not yet rely on this feature.

Conclusion

I’m very fascinated by this interoperability, particularly because of how it offers a balanced compromise for front-end developers in conflict about which tool to use. Vuera highlights the irony in the arguments about which framework is better by demonstrating how they can actually both work in the same build pretty conveniently. I think being able to leverage the ability to use resources and components from both ecosystems could prove to be extremely advantageous over the individual constituents. If you run into issues with a certain component, the selection of has been extended enough to significantly increase the likelihood of something in one of the two ecosystems to work for you. I will continue to post more on this topic and include more examples of how to take advantage of this interoperability for developers who would like to gradually migrate parts of their system or are obligated to use their work with their less comfortable tool.

I sincerely hope you enjoyed this post and look forward to any feedback! Keep a lookout for my future posts as I expand upon this integration.

--

--

Cedric McKinnie

Self-Employed Full-stack Engineer and DevOps Professional, proficient in Cloud-based web development. Interested in React, AWS (Terraform), Docker/Vagrant