webpack loader for Vue Single-File Components
vue-loader is the essential webpack loader for building Vue.js applications using Single-File Components (SFCs). It enables webpack to parse `.vue` files, allowing developers to structure their components with separate `<template>`, `<script>`, and `<style>` blocks, each of which can leverage other webpack loaders (e.g., Sass for styles, Pug for templates, TypeScript for scripts). The current stable version is 17.4.2, which primarily targets Vue 3 and webpack 5, though it retains some compatibility with webpack 4. The project maintains an active release cadence, frequently publishing bug fixes and feature enhancements. Key differentiators include its deep integration with the Vue SFC specification, providing features like scoped CSS, custom blocks, efficient asset handling, and state-preserving hot-reloading for an optimized development experience, making it an indispensable part of most Vue CLI and custom webpack setups.
Common errors
-
Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
cause Webpack encountered a `.vue` file but does not have a rule configured to use `vue-loader` for it.fixAdd a rule to your `webpack.config.js` `module.rules` array: `{ test: /\.vue$/, loader: 'vue-loader' }`. -
TypeError: VueLoaderPlugin is not a constructor
cause The `VueLoaderPlugin` was either imported incorrectly (e.g., as a default import when it's a named export) or instantiated without the `new` keyword.fixEnsure you `import { VueLoaderPlugin } from 'vue-loader';` and use `new VueLoaderPlugin()` in your `webpack.config.js` plugins array. -
Error: [vue-loader] vue-loader currently only supports Vue 2. If you are using Vue 3, you should use vue-loader@^16.0.0 instead.
cause You are attempting to use an older version of `vue-loader` (v15 or earlier) with a Vue 3 project.fixUpgrade `vue-loader` to version 16 or newer (e.g., `npm install vue-loader@latest --save-dev`). -
[vue-loader] vue-loader requires @vue/compiler-sfc (since v16) to be installed.
cause Your `vue-loader` version (v16+) requires the `@vue/compiler-sfc` package, which is missing from your project dependencies.fixInstall `@vue/compiler-sfc`: `npm install @vue/compiler-sfc --save-dev`.
Warnings
- breaking The `refSugar` option was removed in vue-loader v16+ (which supports Vue 3). Projects using this option will break and must migrate to Vue's built-in `reactivityTransform` feature instead.
- breaking vue-loader v16+ (including the current v17 series) is primarily designed for Vue 3. Using vue-loader v17 with Vue 2 projects will lead to compilation errors and incompatibility. For Vue 2 projects, `vue-loader` v15 (e.g., v15.11.1) is required.
- gotcha The `experimentalInlineMatchResource` option is only available in `vue-loader` v17.2.1+ and requires webpack 5. Using it with earlier versions of `vue-loader` or with webpack 4 will not have any effect or may cause configuration errors.
- gotcha When using TypeScript with `<script lang="ts">` in SFCs and `enableTsInTemplate: true` (the default since v16.8+), `ts-loader` can cause template hot-reloading issues due to its cache invalidation. This can result in full component reloads even for minor template edits.
- gotcha The `VueLoaderPlugin` must always be included in your webpack configuration's `plugins` array. Forgetting to add `new VueLoaderPlugin()` will prevent `vue-loader` from correctly processing custom blocks and other essential features of Vue Single-File Components, leading to parsing errors or unexpected behavior.
Install
-
npm install vue-loader -
yarn add vue-loader -
pnpm add vue-loader
Imports
- VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin;import { VueLoaderPlugin } from 'vue-loader'; - Rule for .vue files
{ test: /\.vue$/, use: ['vue-loader'] }{ test: /\.vue$/, loader: 'vue-loader' } - TypeScript support
No specific `wrong` for this, but omitting `appendTsSuffixTo` can lead to issues.
{ test: /\.ts$/, loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/] } }
Quickstart
/* webpack.config.js */
import { VueLoaderPlugin } from 'vue-loader';
import path from 'path';
export default {
mode: 'development',
entry: './src/main.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [require.resolve('./src/App.vue')], // Path to main vue component or [/\.vue$/]
},
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader',
],
},
],
},
plugins: [
new VueLoaderPlugin(),
],
resolve: {
extensions: ['.vue', '.js', '.ts'],
},
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 8080,
hot: true,
},
};
/* src/main.ts */
import { createApp } from 'vue';
import App from './App.vue';
import './assets/main.css';
const app = createApp(App);
app.mount('#app');
<!-- src/App.vue -->
<template>
<div class="container">
<h1>{{ greeting }}</h1>
<p>This is a Vue Single-File Component processed by vue-loader.</p>
<button @click="increment">Count: {{ count }}</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'App',
setup() {
const greeting = ref('Hello, vue-loader!');
const count = ref(0);
const increment = () => {
count.value++;
};
return {
greeting,
count,
increment,
};
},
});
</script>
<style scoped>
.container {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
border: 1px solid #ddd;
padding: 20px;
border-radius: 8px;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
h1 {
color: #42b983;
}
button {
background-color: #42b983;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-top: 15px;
}
button:hover {
background-color: #368a6f;
}
</style>
/* src/assets/main.css */
body {
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Loader Quickstart</title>
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>