'vue.runtime.esm-browser.js does not render Vue 3 components

I created a vue 3 project using Vue cli. I am using a webpack config to manage my build. When I point my vue bundle to vue.runtime.esm-browser.js, then I get a warning in browser console. "[Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Use "vue.esm-browser.js" instead."

When I checked the docs, it was mentioned as "vue-loader" plugin converts the html template to render functions. Looks like I am missing something which is needed to webpack.

Entry file : main.js

import { createApp } from "vue";
import corecomponentA from "../core/components/corecomponentA.vue";

createApp({
  components: {
    "core-component-a": corecomponentA,
  },
}).mount("#app");

Webpack.config.js

var path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;
const WebpackBar = require("webpackbar");

module.exports = (env, options) => {
  const devMode = options.mode != "production";
  return {
    entry: {
      "vue-bundle-store": "./src/entry/main.js",
    },
    output: {
      path: path.resolve(
        __dirname,
        "./../ui.clientlibs/src/js/"
      ),
      filename: "[name].js",
      chunkFilename: "[name].js",
      publicPath: process.env.BASE_URL,
    },

    module: {
      rules: [
        {
          enforce: "pre",
          test: /\.js$/,
          exclude: /node_modules/,
          loader: "eslint-loader",
        },
        {
          test: /\.vue$/,
          loader: "vue-loader",
        },
        {
          test: /\.js$/,
          loader: "babel-loader",
          exclude: "/node_modules/",
          query: {
            presets: ["@babel/preset-env"],
          },
        },
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: [
            {
              loader: "babel-loader",
              options: { babelrc: true },
            },
            {
              loader: "ts-loader",
              options: { appendTsSuffixTo: [/\.vue$/] },
            },
          ],
        },
      ],
    },
    stats: {
      colors: true,
    },
    optimization: {
      splitChunks: {
        cacheGroups: {
          commons: {
            test: /[\\/]node_modules[\\/]/,
            name: "vendor-bundle",
            chunks: "all",
          },
        },
      },
      minimizer: !devMode
        ? [
            new UglifyJsPlugin({
              sourceMap: false,
              uglifyOptions: {
                chunkFilter: (chunk) => {
                  if (chunk.name === "vendor-bundle") {
                    return false;
                  }

                  return true;
                },
                compress: {
                  drop_console: true,
                },
                mangle: {
                  reserved: ["vueIns", "args", "el"],
                },
              },
            }),
          ]
        : [],
    },
    devtool: "source-map",    
    plugins: [      
      new CleanWebpackPlugin(),
      new VueLoaderPlugin(),
      new WebpackBar(),
      new BundleAnalyzerPlugin({
        analyzerPort: 4000,
        openAnalyzer: false,
        analyzerMode: "static",
      }),
    ] ,
    resolve: {
      extensions: [".ts", ".js", ".vue", ".json"],
      alias: {                
       vue: devMode ? "vue/dist/vue.runtime.esm-browser.js" : "vue/dist/vue.runtime.esm-browser.prod.js"
      }
    } 
  };
};

coreComponentA.vue

<script lang="ts">
import { h, ref, reactive } from "vue";

export default {
  setup() {
    const str = ref("Core component B");
    const object = reactive({ foo: "bar" });
    return () => h("div", [str.value, object.foo]);
  }
};
</script>

package.json

{
  "name": "vue3.test",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "lint": "vue-cli-service lint",
    "analyze-bundle": "webpack-bundle-analyzer stats.json",
    "bundle": "webpack --mode=production --env.production --config webpack.config.js",
    "bundle-dev": "webpack --mode=development --env.production=false --config webpack.config.js",
    "stats": "webpack --mode=production --env.production --config webpack.config.js --profile --json > stats.json"
  },
  "dependencies": {
    "vue": "^3.0.2"
  },
  "devDependencies": {
    "@types/jest": "^24.0.19",
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-plugin-unit-jest": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.2",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^5.0.2",
    "@vue/test-utils": "^2.0.0-0",
    "clean-webpack-plugin": "^3.0.0",
    "core-js": "^3.6.5",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-vue": "^7.0.0-0",
    "html-webpack-plugin": "^3.2.0",
    "prettier": "^1.19.1",
    "typescript": "~3.9.3",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "vue-jest": "^5.0.0-0",
    "vue-loader": "^16.0.0-beta.8",
    "webpack-cli": "^3.3.10",
    "webpackbar": "^4.0.0"
  }
}

babel.config.js

module.exports = {
  ignore: [/\/core-js/],
  presets: [
    [
      "@babel/preset-env",
      { modules: false, useBuiltIns: "usage", corejs: "3.6.5" },
    ],
  ],
  overrides: [
    {
      test: "./node_modules",
      sourceType: "unambiguous",
    },
  ],
};

Usage of my component in a html file

<div id="app">
<core-component-a></core-component-a>
</div>

The component is not rendered in browser. Instead the below message is displayed.

VM211871:1 [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Use "vue.esm-browser.js" instead. 
  at <App>


Solution 1:[1]

vue-loader converts the html template to render function only in SFC's (Sinle File Components) - .vue files (as you can tell from vue rule in Webpack config) - and only templates provided in <template></template> block of SFC

But you have a template in your HTML file - content of <div id="app"> is essentially Vue template. Runtime + Compiler vs. Runtime-only

Docs vue.esm-bundler.js: includes the runtime compiler. Use this if you are using a bundler but still want runtime template compilation (e.g. in-DOM templates or templates via inline JavaScript strings - component template option).

Also if you using Webpack, you should use "bundler" version of Vue

Webpack.config.js

alias: {                
       vue: "vue/dist/vue.esm-bundler.js"
}

...you don't need to switch minified/dev bundle because Webpack will (when configured correctly) optimize Vue code same way as your own code..

Also, pay attention to this sentence in the docs: Leaves prod/dev branches with process.env.NODE_ENV guards (must be replaced by bundler)

NODE_ENV is conventionally used to define the environment type and is used by Vue to decide what code to include...

Note

I don't really understand why are You using your own Webpack config for project created with Vue CLI when whole point of Vue CLI is to manage webpack config for you and offers plenty of options to customize it...doesn't make any sense

Solution 2:[2]

you just have to replace on Webpack.config.js

alias: {                
       vue: devMode ? "vue/dist/vue.runtime.esm-browser.js" : "vue/dist/vue.runtime.esm-browser.prod.js"
}

With

vue: "vue/dist/vue.esm-bundler.js"

This works for me.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2