importing a package in ES6: "Failed to resolve module specifier "vue""

Trying to follow some Vue tutorials and I can't currently import Vue in a .js file and then import this file in my index.html. This is how I'm importing the script in my index.html:

<script src="./js/main.js" type="module"></script>

If I do this in my main.js file:

import Vue from 'vue';

I get the following error in the browser console:

Uncaught TypeError: Failed to resolve module specifier "vue". Relative references must start with either "/", "./", or "../".

If my import line to:

import Vue from '../../node_modules/vue';

Then I get a different error:

http://localhost:63342/vue-official-tutorial/node_modules/vue net::ERR_ABORTED 404 (Not Found)

What am I doing wrong?


The way you can use ES modules in your Browser directly (as of June 2020) is thus:

  1. Use the ESM version of your dependencies (the one that has import instead of require). For example, Vue ESM version is available at: https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0-beta.14/vue.esm-browser.js

  2. Make your browser work with the experimental importmap feature. Import maps are a new web recommendation, not yet supported in mainstream browsers. https://wicg.github.io/import-maps/#import-map In Chrome this is under chrome://flags#enable-experimental-productivity-features (latest Chrome versions moved this under chrome://flags#enable-experimental-web-platform-features)

  3. Create an importmap in your HTML file. It only works with inline <script> tags at the moment in Chrome. For example:

<script type="importmap">
{ "imports": {
  "vue":        "https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0-beta.14/vue.esm-browser.js",
  "vue-router": "https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.0-alpha.12/vue-router.esm.js",
  "vuex":       "https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.0-beta.2/vuex.esm-browser.js"
} }
</script>
  1. Load your own code as an ESM module.
<script type="module" src="./main.js"></script>
  1. In your own scripts, and the scripts that you import - you can now successfully import from named modules.

Full example:

<html>
<body>
<script type="importmap">
{ "imports": {
  "vue":        "https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0-beta.14/vue.esm-browser.js",
  "vue-router": "https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.0-alpha.12/vue-router.esm.js",
  "vuex":       "https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.0-beta.2/vuex.esm-browser.js"
} }
</script>
<script type="module">
import { createRouter, createWebHistory } from 'vue-router'
import { createApp } from 'vue'

const router = createRouter()

export default createApp({
  router
})
</script>
</body>
</html>

UPDATE (2020-05-10)

Using ES6 modules without Webpack


If you are working with ES6 then you should NOT manually inserting your main.js into index.html - this will be handled by Webpack. Actually, the simplest tutorial for Vue goes like this:

  1. npm install -g vue-cli
  2. vue init webpack my_project
  3. npm run dev (and start developing - result is available on http://localhost:8080)
  4. npm run build (result is available inside the ./dist folder of your project

Also, you should import Vue like this

import Vue from 'vue';

and not like this

import Vue from '../../node_modules/vue';

EDIT

Okay, if you insist on going through the beginners' path and not using Webpack and single-file Vue components - then you should start like this:

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <title>My beginners project</title>
    <link rel="stylesheet" type="text/css" href="/assets/css/styles.css" />
  </head>

  <body>
    <div id="app">
      <router-view></router-view>
    </div>
    <!-- templates for your components -->
    <template id="login">
      <div>test</div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/vue-router.js"></script>
    <!-- code for your components -->
    <script type="text/javascript" src="/app/login.js"></script>
    <!-- Vue Root component should be last -->
    <script type="text/javascript" src="/app/app.js"></script>
  </body>

</html>

And your /app/app.js will look like this:

var badRoute = Vue.component('bad-route', {
    template: '<div id="bad_route"><h1>Page Not Found</h1><p>Sorry, but the page you were trying to view does not exist.</p></div>'
});
var vue_router = new VueRouter({
    base: '/app'
    , mode: 'hash'
    , routes: [{
        path: '/'
        , redirect: '/login'
    }, {
        path: '/login'
        , component: loginForm
        , name: 'LOGIN'
    }, {
        path: '*', // should be last, otherwise matches everything
        component: badRoute
        , name: 'NOT FOUND'
    }]
});
// Main application
var vue_app = new Vue({
        router: vue_router
    , })
    .$mount('#app');

And your /app/login.js component will look like this:

var loginForm = Vue.component('login-form', {
    template: '#login', // should match the ID of template tag
    data: function() {
        var a = {
            username: ''
            , password: ''
        , };
        return a;
    }
    , methods: {}
});

You can only use "import vue..." if you are using the CLI and webpack, etc.

If you are using Vue directly in a web page, you can follow the instructions at https://vuejs.org/v2/guide/installation.html#Direct-lt-script-gt-Include and include a line like this in your HTML file:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

Do this before you import your own script, and then Vue will be defined and you don't need to import it again. Import your script without the "module" attribute. In the script, you can run:

var x = new Vue({ 
  el: '#myApp',
  ... all the other stuff ...
})

This assumes that somewhere on your HTML page you have:

<div id=myApp></div>