Tensorflow.js NLP QNA Model with Vue 3

Mert Gozukara
4 min readAug 27, 2021


Lately I have been experimenting with machine learning and my curiosity was how far I could go with Javascript and Vue.

I believe most of you heard Tensorflow.js already. In this example we will build a Vue 3 app using Tensorflow’s Natural Language Question Answering model powered by BERT.

Here are the requirements:

  • Creating a VUE 3 App
  • Install dependencies from Tensorflow.js
  • Importing dependencies from Tensorflow.js
  • Loading our QNA Model
  • Preparing necessary UI elements to retrieve data from user
  • Passing the data from UI elements to necessary QNA methods
  • Showing the highest scored answers in our UI

Let’s start with the first step..

To create a VUE 3 App we are receving support from vue-cli and using this command on our terminal to complete the step

vue create tensorflow-qna

Afterwards we will see multiple options to choose on how to configure our vue app before creating it.

I have a preset from previous projects so I would choose:

  • Vue 3
  • Node-sass
  • Babel
  • Router
  • Vuex
  • Es-lint
  • Unit-jest

When this operation is done our terminal will show the result and we can navigate to our project to serve it.

Now we need to install our dependencies so…

yarn add @tensorflow/tfjs @tensorflow-models/qna

Once this task is completed our next step is to import our dependencies from Tensorflow.js

Since this is just a test example you do not need to structure your project therefore I believe it is fairly okay to use pre built component called Helloword.vue to work on.

To import our dependencies we navigate to Helloworld.vue component and add these after our script tag.

import * as tf from '@tensorflow/tfjs'
import * as qna from '@tensorflow-models/qna'

Okay we have installed and imported our dependencies. Now it is time to work on our model and our methods to pass data/retrieve data from it.

We need to load our model once when this component is instantiated therefore correct lifecycle method would be ‘mounted’.

First we create our method in the methods object. And it looks like this.

async loadModel() {
this.loadedModel = Object.freeze(await qna.load())
const tensorflow = tf
console.log('tensorflow : ', tensorflow);
console.log('qna-model : ', this.loadedModel);},

Here we consolled our tensorflow object to see it’s properties and the most important one is to load our qna model.

One trick we are applying here is to use Object.freeze. If you do not use this, since Vue is using a wrapper to our object Tensorflow will throw an error and you will not be able to pass or retrieve data to required model.

Now it is time to add this method to our mounted lifecycle.

mounted() {

If you do this step once your Helloworld component is awake you will see two consoles — Qna Model and Tensorflow Object —

So the next step would be to pass and retrieve data using findAnswers method of our QNA Model.

To do this we are creating another method as answerQuestion and it looks like this:

async answerQuestion() {
if(this.loadedModel !== null) {
console.log('this.loadedModel : ', this.loadedModel);
console.log('this.question : ', this.question);
console.log('this.passage : ', this.passage);
this.answers = await this.loadedModel.findAnswers(this.question, this.passage)

Now here we check if our model is there and if so we are inside the block where the operation of findingAnswers can be completed. To accomplish this we need 2 data sources. One is our question block and the second is our passage block where our model could get the required data source to quickly understand where is the answer of our possible question.

After setting these methods now we need to cover how to retrieve this question and passage part of our function.

We will use ‘data(){}’ from Vue and store couple variables inside.

data() {
return {
loadedModel: '',
answers: [],
question: '',
passage: ''

Our data is looking like this in the component.

Now we need HTML Elements to connect these variables so they can store data from user’s inputs.

<div class="home">
<div class="home__block">
<textarea name="" id="" cols="30" rows="10" ref="passage" v-model="passage">

<div class="home__block">
<input type="text" ref="question" @keyup.enter="answerQuestion" v-model="question">

<div class="home__block">
<div class="home__block-answer" v-for="(answer, id) in answers" :key="answer">
<span > {{ id + 1 }} : {{ answer.text }} ----- {{ answer.score }}</span>


Our HTML is looking like this. To quickly overview what is happening here we need to understand our requirements.

First of all we need a textarea for user to either copy paste or write the passage.

Then we need an input element for user to ask the question.

And finally we need our answers block where we are looping outcomes in this case answers and printing them inside of our block with their indexes and scores.

One of the key parts here is to use v-model so we can always keep track of entered data in connected UI elements. This was the reason why we keep couple variables in our data function.

Lastly to make it look a little better we can use couple scss code like this.

.home {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

&__block {
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;

&__block-answer {
margin-top: 8px;
font-size: 18px;

textarea {
border-radius: 8px;
width: 600px;
height: 300px;
outline: none;
padding: 12px 16px;
font-size: 16px;

input {
border-radius: 4px;
padding: 18px 16px;
width: 600px;
font-size: 16px;
outline: none;

h2, span {
color: whitesmoke;

span {
margin-top: 8px;

It is not the best practice of using css but this is not the highlight of our article therefore it will be just enough.

Now if you serve your project you will see the result in your screen.

I will drop my github repo link here for you to check the whole project.

You can also check the demo here.

Hope you enjoyed it!!



Mert Gozukara