Building a Vue 3 component with Typescript
Tags: vue, typescript, webdev, vue3
Published: 2020-03-09 17:45:48

The alpha version of [Vue 3](https://github.com/vuejs/vue-next) has been available for some time now!
While I’m writing this post the alpha 8 version has just been released.

The new version of Vue will bring with it an avalanche of enhancements and improvements but the most significant changes in the new version will be:
- Composition API: this is a much discussed feature inspired by the React hooks.
- Portals: render certain content outside the current component.
- Fragments: allows multiple root nodes.
- Updated v-model-API: it now accepts multiple models.
- Suspense: is a special component that renders a fallback content instead of your component until a condition is met (mostly for UX stuff).
- TypeScript: Vue now has full TypeScript support.

Since Vue 3 is still under active development some of its components may still have bugs or some API may change but it is already possible to start writing a simple application to start playing with the new features.

Because I’m a big fan of TypeScript in this article I will describe you the steps that I followed to to create a new application with Vue 3 using TypeScript.
But talk is cheap, let’s start setting up a new app 🙂

##  Setting up the project 
The first thing to do is to initialize a new application using the command on the next line:
```
yarn init
```
Next, add the dependencies needed by the project:

```
yarn add vue@3.0.0-alpha.8
yarn add --dev yarn vue-loader@v16.0.0-alpha.3 webpack-cli webpack webpack-dev-server typescript ts-loader @vue/compiler-sfc@v3.0.0-alpha.8
```

Now you have to define a simple webpack configuration creating the file `webpack.config.js` and adding the following code:

```JS
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = (env = {}) => ({
  mode: env.prod ? 'production' : 'development',
  devtool: env.prod ? 'source-map' : 'cheap-module-eval-source-map',
  entry: path.resolve(__dirname, './src/main.ts'),
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: 'vue-loader'
      },
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/],
        }
      },
    ]
  },
  resolve: {
    extensions: ['.ts', '.js', '.vue', '.json'],
    alias: {
      'vue': '@vue/runtime-dom'
    }
  },
  plugins: [
    new VueLoaderPlugin(),
  ],
  devServer: {
    inline: true,
    hot: true,
    stats: 'minimal',
    contentBase: __dirname,
    overlay: true
  }
})
```

So far so good, our setup is coming together, but it won’t compile TypeScript just yet, for that we need to add a `tsconfig.json` file with the following rules:

```json
{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "declaration": false,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "module": "es2015",
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noLib": false,
    "sourceMap": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es2015",
    "baseUrl": ".",
  },
  "exclude": [
    "./node_modules"
  ],
  "include": [
    "./src/**/*.ts",
    "./src/**/*.vue",
  ],
}
```

After configuring Typescript and Webpack it is time to add a shortcut to start your application adding a new script to the `package.json` file:

```json
{
  
  //...
  // Dependencies
  //...

  "scripts": {
    "dev": "webpack-dev-server"
  }
}
```

*NOTE:* to avoid error when you will import `*.vue` files you have to add the following `shims-vue.d.ts` file in `./src` folder:

```ts
declare module "*.vue" {
    import { defineComponent } from "vue";
    const Component: ReturnType<typeof defineComponent>;
    export default Component;
}
```

To test that the infrastructure built so far is working correctly you will need some things:
- Have a simple `index.html` in the root of the project 
```HTML
<!-- index.html -->
<h1>Hello Vue 3!</h1>
<script src="/dist/main.js"></script>
```
- Have a `/src` folder 
- Have `main.ts` file to the project as follows:
```ts
// src/main.ts

console.log('Hello world from Typescript!');

```
- Run the web server with `yarn dev`

If everything is working then connecting to `http://localhost:8080` you should see the page we have just created.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ye1ijcbx5ug854u5z3rh.jpg)

To recap, at the end of these steps you should have the following architecture:
```
├── index.html
├── package.json
├── tsconfig.json
├── webpack.config.js
├── src
│    ├── shims-vue.d.ts
│    └── main.ts
```

## Let’s build a component
Now that the environment needed to build the application is finally ready you can start creating your first Vue 3 component.

First of all, add a new file called `App.vue` inside the `src` folder as follows:

```HTML
<template>
  <h2>This is a Vue 3 component!</h2>
  <button @click="increase">Clicked {{ count }} times.</button>
</template>
<script lang="ts">
import {defineComponent, ref} from "vue";
export default defineComponent({
  setup() {
    const count = ref(0)
    const increase = () => {
      count.value++
    }
    
    return {
      count,
      increase,
    }
  }
});
</script>
```

As you can see, compared to Vue 2 in which to create a new Vue component it was necessary to create a Typescript class and extend Vue using `class MyClass extends Vue {}`, now Vue 3 offers a `defineComponent()` function.
Inside the `defineComponent()` function you can see a `setup` function, which takes the props as the first argument. Since in this case the App component will be a top level component, no props will be passed (so I have omitted to pass them on).
Also, as you can see from the code, whatever is returned by the `setup()` method, it is then accessible from the template.

Now that the Vue component has been created, you just have to import it into the `main.ts` file as follows:

```ts
import {createApp} from 'vue';
import App from './App.vue';

createApp(App).mount('#app');
```


Also in this case you can see how compared to the previous version of Vue it is no longer necessary to initialize the new application with `const app = new Vue (....).$Mount('# app')` but with Vue 3 it is possible to use the function `createApp()` and the `mount()` method to bind the application to a DOM selector.

Finally, the last step is to edit the `index.html` file to include the selector specified to Vue in the previous step:

```HTML
<h1>Hello Vue 3!</h1>

<div id="app"></div>

<script src="/dist/main.js"></script>
```


At this point, relaunching the application with `yarn dev` you can start playing with the new Vue component just created.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ri6iqdvbnp6jo2bgzog3.gif)

## Recap
In this post I showed you how to create a very simple component using Vue 3, the composition API and Typescript. Obviously I have scratched just the tip of the iceberg and there are a thousand other features to try in Vue 3 but already with this simple component it is possible to appreciate the new "functional" approach with which it is possible to define the components in the next release of Vue.

PS: All the code is available on [GitHub](https://github.com/lmillucci/vue3-typescript-component).

---

Feel free to reach out to me! [Blog (in italian)](https://www.lorenzomillucci.it/) || [Twitter](https://twitter.com/LorenzoMillu) || [GitHub](https://github.com/lmillucci) || [LinkedIn](https://www.linkedin.com/in/lorenzo-millucci/)