diff --git a/packages/nuxt3/src/app/_templates/entry.client.ts b/packages/nuxt3/src/app/_templates/entry.client.js
similarity index 91%
rename from packages/nuxt3/src/app/_templates/entry.client.ts
rename to packages/nuxt3/src/app/_templates/entry.client.js
index 9435a5b65b..c8e2507257 100644
--- a/packages/nuxt3/src/app/_templates/entry.client.ts
+++ b/packages/nuxt3/src/app/_templates/entry.client.js
@@ -1,3 +1,4 @@
+<%= nuxtOptions.vite ? "import('vite/dynamic-import-polyfill')" : '' %>
 import { createSSRApp, nextTick } from 'vue'
 import { createNuxt, applyPlugins } from 'nuxt/app/nuxt'
 import plugins from './plugins'
diff --git a/packages/nuxt3/src/app/_templates/entry.server.ts b/packages/nuxt3/src/app/_templates/entry.server.js
similarity index 99%
rename from packages/nuxt3/src/app/_templates/entry.server.ts
rename to packages/nuxt3/src/app/_templates/entry.server.js
index 973ce850fa..3387299b89 100644
--- a/packages/nuxt3/src/app/_templates/entry.server.ts
+++ b/packages/nuxt3/src/app/_templates/entry.server.js
@@ -1,5 +1,4 @@
 import { createApp } from 'vue'
-
 import { createNuxt, applyPlugins } from 'nuxt/app/nuxt'
 import plugins from './plugins'
 import serverPlugins from './plugins.server'
diff --git a/packages/nuxt3/src/app/_templates/plugins.client.ts b/packages/nuxt3/src/app/_templates/plugins.client.js
similarity index 100%
rename from packages/nuxt3/src/app/_templates/plugins.client.ts
rename to packages/nuxt3/src/app/_templates/plugins.client.js
diff --git a/packages/nuxt3/src/app/_templates/plugins.ts b/packages/nuxt3/src/app/_templates/plugins.js
similarity index 100%
rename from packages/nuxt3/src/app/_templates/plugins.ts
rename to packages/nuxt3/src/app/_templates/plugins.js
diff --git a/packages/nuxt3/src/app/_templates/plugins.server.ts b/packages/nuxt3/src/app/_templates/plugins.server.js
similarity index 100%
rename from packages/nuxt3/src/app/_templates/plugins.server.ts
rename to packages/nuxt3/src/app/_templates/plugins.server.js
diff --git a/packages/nuxt3/src/app/_templates/views/app.template.html b/packages/nuxt3/src/app/_templates/views/app.template.html
index 6fc4ebb42c..425fa2bbea 100644
--- a/packages/nuxt3/src/app/_templates/views/app.template.html
+++ b/packages/nuxt3/src/app/_templates/views/app.template.html
@@ -5,5 +5,7 @@
   </head>
   <body {{ BODY_ATTRS }}>
     <div id="__nuxt">{{ APP }}</div>
+    <% if (nuxtOptions.vite && nuxtOptions.dev) { %><script type="module" src="/@vite/client"></script>
+    <script type="module" src="/entry.client.js"></script><% } %>
   </body>
 </html>
diff --git a/packages/nuxt3/src/app/composables/fetch.ts b/packages/nuxt3/src/app/composables/fetch.ts
index 6752c4743f..e537763f52 100644
--- a/packages/nuxt3/src/app/composables/fetch.ts
+++ b/packages/nuxt3/src/app/composables/fetch.ts
@@ -1,6 +1,6 @@
 import { Ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue'
 import { Nuxt, useNuxt } from 'nuxt/app'
-import { httpFetch } from '../utils/fetch'
+import { $fetch } from 'ohmyfetch'
 import { useData } from './data'
 
 export type HTTPRequest = string | { method: string, url: string }
@@ -44,7 +44,7 @@ export function useFetch (defaults?: FetchOptions) {
     options = {
       server: true,
       defer: false,
-      fetcher: httpFetch,
+      fetcher: globalThis.$fetch || $fetch,
       ...defaults,
       ...options
     }
diff --git a/packages/nuxt3/src/app/utils/fetch.ts b/packages/nuxt3/src/app/utils/fetch.ts
deleted file mode 100644
index eb79b844ae..0000000000
--- a/packages/nuxt3/src/app/utils/fetch.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// TODO: Move to a nuxt-contrib utility
-import destr from 'destr'
-
-// TODO: polyfill by env (nuxt) not by util
-const _fetch = process.server ? require('node-fetch') : global.fetch
-
-export async function httpFetch (path: string) {
-  const res = await _fetch(path)
-  if (!res.ok) {
-    throw new Error(res)
-  }
-  const data = await res.text()
-
-  return destr(data)
-}
diff --git a/packages/nuxt3/src/builder/builder.ts b/packages/nuxt3/src/builder/builder.ts
index 6132c4bc20..4445b5b925 100644
--- a/packages/nuxt3/src/builder/builder.ts
+++ b/packages/nuxt3/src/builder/builder.ts
@@ -1,7 +1,6 @@
 import { join, relative } from 'path'
 import fsExtra from 'fs-extra'
 import { debounce } from 'lodash'
-import { BundleBuilder } from 'src/webpack'
 import { Nuxt } from '../core'
 import { DeterminedGlobals, determineGlobals } from '../utils'
 import {
@@ -73,6 +72,11 @@ function watch (builder: Builder) {
   appWatcher.watch(/^(A|a)pp\.[a-z]{2,3}/, refreshTemplates, ['add', 'unlink'])
   // Watch for page changes
   appWatcher.watch(new RegExp(`^${nuxt.options.dir.pages}/`), refreshTemplates, ['add', 'unlink'])
+
+  // Shared Watcher
+  const watchHookDebounced = debounce((event, file) => builder.nuxt.callHook('builder:watch', event, file), 100)
+  appWatcher.watchAll(watchHookDebounced)
+  nuxtAppWatcher.watchAll(watchHookDebounced)
 }
 
 export async function generate (builder: Builder) {
@@ -90,5 +94,11 @@ export async function generate (builder: Builder) {
 }
 
 async function bundle ({ nuxt }: Builder) {
-  await new BundleBuilder(nuxt).build()
+  // @ts-ignore
+  const useVite = !!nuxt.options.vite
+  const bundle = await (useVite
+    ? import('./vite/vite' /* webpackChunkName: "vite" */)
+    : import('./webpack/webpack' /* webpackChunkName: "webpack" */))
+    .then(p => p.bundle)
+  return bundle(nuxt)
 }
diff --git a/packages/nuxt3/src/builder/template.ts b/packages/nuxt3/src/builder/template.ts
index fe09f160cf..138a7ddcdb 100644
--- a/packages/nuxt3/src/builder/template.ts
+++ b/packages/nuxt3/src/builder/template.ts
@@ -12,7 +12,8 @@ export interface NuxtTemplate {
 export function templateData (builder) {
   return {
     globals: builder.globals,
-    app: builder.app
+    app: builder.app,
+    nuxtOptions: builder.nuxt.options
   }
 }
 
diff --git a/packages/nuxt3/src/builder/vite/vite.ts b/packages/nuxt3/src/builder/vite/vite.ts
new file mode 100644
index 0000000000..5df495c0f8
--- /dev/null
+++ b/packages/nuxt3/src/builder/vite/vite.ts
@@ -0,0 +1,129 @@
+import { resolve } from 'path'
+import { Nuxt } from 'src/core'
+import { mkdirp, writeFile } from 'fs-extra'
+import createRequire from 'create-require'
+import vue from '@vitejs/plugin-vue'
+import consola from 'consola'
+import * as vite from 'vite'
+
+interface ViteBuildContext {
+  nuxt: Nuxt
+  config: vite.InlineConfig
+}
+
+export async function bundle (nuxt: Nuxt) {
+  const ctx: ViteBuildContext = {
+    nuxt,
+    config: {
+      root: nuxt.options.buildDir,
+      mode: nuxt.options.dev ? 'development' : 'production',
+      alias: {
+        'nuxt/app': nuxt.options.appDir,
+        '~build': nuxt.options.buildDir,
+        '~': nuxt.options.srcDir,
+        '@': nuxt.options.srcDir
+      },
+      clearScreen: false,
+      plugins: [
+        vue({})
+      ],
+      build: {
+        emptyOutDir: false
+      }
+    }
+  }
+
+  await mkdirp(nuxt.options.buildDir)
+  const { dependencies = {}, devDependencies = {} } = createRequire(nuxt.options.rootDir)('package.json')
+  await writeFile(resolve(nuxt.options.buildDir, 'package.json'), JSON.stringify({
+    private: true,
+    description: 'auto generated',
+    devDependencies: { ...dependencies, ...devDependencies }
+  }, null, 2))
+
+  const callBuild = async (fn, name) => {
+    try {
+      const start = Date.now()
+      await fn(ctx)
+      const time = (Date.now() - start) / 1000
+      consola.success(`${name} compiled successfully in ${time}s`)
+    } catch (err) {
+      consola.error(`${name} compiled with errors:`, err)
+    }
+  }
+
+  if (nuxt.options.dev) {
+    await Promise.all([
+      callBuild(buildClient, 'Client'),
+      callBuild(buildServer, 'Server')
+    ])
+  } else {
+    await callBuild(buildClient, 'Client')
+    await callBuild(buildServer, 'Server')
+  }
+}
+
+async function buildClient (ctx: ViteBuildContext) {
+  const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
+    define: {
+      'process.server': false,
+      'process.client': true
+    },
+    build: {
+      outDir: 'dist/client',
+      assetsDir: '.',
+      ssrManifest: true,
+      manifest: true,
+      rollupOptions: {
+        input: resolve(ctx.nuxt.options.buildDir, './entry.client.js')
+      }
+    },
+    server: {
+      middlewareMode: true
+    }
+  } as vite.InlineConfig)
+
+  if (ctx.nuxt.options.dev) {
+    const viteServer = await vite.createServer(clientConfig)
+    await ctx.nuxt.callHook('server:devMiddleware', (req, res, next) => {
+      // Workaround: vite devmiddleware modifies req.url
+      const originalURL = req.url
+      viteServer.middlewares.handle(req, res, (err) => {
+        req.url = originalURL
+        next(err)
+      })
+    })
+  } else {
+    await vite.build(clientConfig)
+  }
+}
+
+async function buildServer (ctx: ViteBuildContext) {
+  const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
+    define: {
+      'process.server': true,
+      'process.client': false,
+      window: undefined
+    },
+    build: {
+      outDir: 'dist/server',
+      ssr: true,
+      rollupOptions: {
+        input: resolve(ctx.nuxt.options.buildDir, './entry.server.js')
+      }
+    }
+  } as vite.InlineConfig)
+
+  const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
+  await mkdirp(serverDist)
+  await writeFile(resolve(serverDist, 'client.manifest.json'), 'false')
+  await writeFile(resolve(serverDist, 'server.js'), 'const entry = require("./entry.server.js"); module.exports = entry.default || entry;')
+
+  await vite.build(serverConfig)
+
+  if (ctx.nuxt.options.dev) {
+    ctx.nuxt.hook('builder:watch', () => {
+      vite.build(serverConfig).catch(consola.error)
+    })
+  }
+}
diff --git a/packages/nuxt3/src/webpack/configs/client.ts b/packages/nuxt3/src/builder/webpack/configs/client.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/configs/client.ts
rename to packages/nuxt3/src/builder/webpack/configs/client.ts
diff --git a/packages/nuxt3/src/webpack/configs/index.ts b/packages/nuxt3/src/builder/webpack/configs/index.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/configs/index.ts
rename to packages/nuxt3/src/builder/webpack/configs/index.ts
diff --git a/packages/nuxt3/src/webpack/configs/server.ts b/packages/nuxt3/src/builder/webpack/configs/server.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/configs/server.ts
rename to packages/nuxt3/src/builder/webpack/configs/server.ts
diff --git a/packages/nuxt3/src/webpack/plugins/externals.ts b/packages/nuxt3/src/builder/webpack/plugins/externals.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/plugins/externals.ts
rename to packages/nuxt3/src/builder/webpack/plugins/externals.ts
diff --git a/packages/nuxt3/src/webpack/plugins/vue/client.ts b/packages/nuxt3/src/builder/webpack/plugins/vue/client.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/plugins/vue/client.ts
rename to packages/nuxt3/src/builder/webpack/plugins/vue/client.ts
diff --git a/packages/nuxt3/src/webpack/plugins/vue/server.ts b/packages/nuxt3/src/builder/webpack/plugins/vue/server.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/plugins/vue/server.ts
rename to packages/nuxt3/src/builder/webpack/plugins/vue/server.ts
diff --git a/packages/nuxt3/src/webpack/plugins/vue/util.ts b/packages/nuxt3/src/builder/webpack/plugins/vue/util.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/plugins/vue/util.ts
rename to packages/nuxt3/src/builder/webpack/plugins/vue/util.ts
diff --git a/packages/nuxt3/src/webpack/plugins/warning-ignore.ts b/packages/nuxt3/src/builder/webpack/plugins/warning-ignore.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/plugins/warning-ignore.ts
rename to packages/nuxt3/src/builder/webpack/plugins/warning-ignore.ts
diff --git a/packages/nuxt3/src/webpack/presets/assets.ts b/packages/nuxt3/src/builder/webpack/presets/assets.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/assets.ts
rename to packages/nuxt3/src/builder/webpack/presets/assets.ts
diff --git a/packages/nuxt3/src/webpack/presets/babel.ts b/packages/nuxt3/src/builder/webpack/presets/babel.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/babel.ts
rename to packages/nuxt3/src/builder/webpack/presets/babel.ts
diff --git a/packages/nuxt3/src/webpack/presets/base.ts b/packages/nuxt3/src/builder/webpack/presets/base.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/base.ts
rename to packages/nuxt3/src/builder/webpack/presets/base.ts
diff --git a/packages/nuxt3/src/webpack/presets/esbuild.ts b/packages/nuxt3/src/builder/webpack/presets/esbuild.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/esbuild.ts
rename to packages/nuxt3/src/builder/webpack/presets/esbuild.ts
diff --git a/packages/nuxt3/src/webpack/presets/node.ts b/packages/nuxt3/src/builder/webpack/presets/node.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/node.ts
rename to packages/nuxt3/src/builder/webpack/presets/node.ts
diff --git a/packages/nuxt3/src/webpack/presets/nuxt.ts b/packages/nuxt3/src/builder/webpack/presets/nuxt.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/nuxt.ts
rename to packages/nuxt3/src/builder/webpack/presets/nuxt.ts
diff --git a/packages/nuxt3/src/webpack/presets/pug.ts b/packages/nuxt3/src/builder/webpack/presets/pug.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/pug.ts
rename to packages/nuxt3/src/builder/webpack/presets/pug.ts
diff --git a/packages/nuxt3/src/webpack/presets/style.ts b/packages/nuxt3/src/builder/webpack/presets/style.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/style.ts
rename to packages/nuxt3/src/builder/webpack/presets/style.ts
diff --git a/packages/nuxt3/src/webpack/presets/vue.ts b/packages/nuxt3/src/builder/webpack/presets/vue.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/presets/vue.ts
rename to packages/nuxt3/src/builder/webpack/presets/vue.ts
diff --git a/packages/nuxt3/src/webpack/utils/config.ts b/packages/nuxt3/src/builder/webpack/utils/config.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/utils/config.ts
rename to packages/nuxt3/src/builder/webpack/utils/config.ts
diff --git a/packages/nuxt3/src/webpack/utils/index.ts b/packages/nuxt3/src/builder/webpack/utils/index.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/utils/index.ts
rename to packages/nuxt3/src/builder/webpack/utils/index.ts
diff --git a/packages/nuxt3/src/webpack/utils/mfs.ts b/packages/nuxt3/src/builder/webpack/utils/mfs.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/utils/mfs.ts
rename to packages/nuxt3/src/builder/webpack/utils/mfs.ts
diff --git a/packages/nuxt3/src/webpack/utils/postcss.ts b/packages/nuxt3/src/builder/webpack/utils/postcss.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/utils/postcss.ts
rename to packages/nuxt3/src/builder/webpack/utils/postcss.ts
diff --git a/packages/nuxt3/src/webpack/utils/reserved-tags.ts b/packages/nuxt3/src/builder/webpack/utils/reserved-tags.ts
similarity index 100%
rename from packages/nuxt3/src/webpack/utils/reserved-tags.ts
rename to packages/nuxt3/src/builder/webpack/utils/reserved-tags.ts
diff --git a/packages/nuxt3/src/webpack/builder.ts b/packages/nuxt3/src/builder/webpack/webpack.ts
similarity index 98%
rename from packages/nuxt3/src/webpack/builder.ts
rename to packages/nuxt3/src/builder/webpack/webpack.ts
index 6de0852265..1368430c16 100644
--- a/packages/nuxt3/src/webpack/builder.ts
+++ b/packages/nuxt3/src/builder/webpack/webpack.ts
@@ -13,7 +13,7 @@ import { createWebpackConfigContext, applyPresets, getWebpackConfig } from './ut
 
 const glob = pify(Glob)
 
-export class WebpackBundler {
+class WebpackBundler {
   nuxt: Nuxt
   plugins: Array<string>
 
@@ -251,3 +251,8 @@ export class WebpackBundler {
     this.nuxt.options.target = TARGETS.static
   }
 }
+
+export function bundle (nuxt: Nuxt) {
+  const bundler = new WebpackBundler(nuxt)
+  return bundler.build()
+}
diff --git a/packages/nuxt3/src/config/config/_common.ts b/packages/nuxt3/src/config/config/_common.ts
index 9dd3ca8c35..28cd98c699 100644
--- a/packages/nuxt3/src/config/config/_common.ts
+++ b/packages/nuxt3/src/config/config/_common.ts
@@ -85,7 +85,8 @@ interface CommonConfiguration {
   _nuxtConfigFile?: string
   alias: Record<string, string>
   appDir: string,
-  buildDir: string
+  buildDir: string,
+  vite: boolean,
   buildModules: NuxtModule[]
   createRequire?: (module: NodeJS.Module) => NodeJS.Require
   debug?: boolean
diff --git a/packages/nuxt3/src/config/load.ts b/packages/nuxt3/src/config/load.ts
index 265801e1b7..195f8370da 100644
--- a/packages/nuxt3/src/config/load.ts
+++ b/packages/nuxt3/src/config/load.ts
@@ -71,11 +71,11 @@ export async function loadNuxtConfig ({
     // Clear cache
     clearRequireCache(configFile)
     const _require = createRequire(module)
-    const _config: Configuration | ((ctx: Record<string, any>) => Promise<Configuration>) = interopDefault(_require(configFile) || {})
+    let _config: Configuration | ((ctx: Record<string, any>) => Promise<Configuration>) = interopDefault(_require(configFile) || {})
 
     if (typeof _config === 'function') {
       try {
-        options = interopDefault(await _config(configContext))
+        _config = interopDefault(await _config(configContext))
       } catch (error) {
         consola.error(error)
         consola.fatal('Error while fetching async configuration')
@@ -83,7 +83,7 @@ export async function loadNuxtConfig ({
     }
 
     // Don't mutate options export
-    options = { ...options }
+    options = { ..._config }
 
     // Keep _nuxtConfigFile for watching
     options._nuxtConfigFile = configFile
diff --git a/packages/nuxt3/src/config/options.ts b/packages/nuxt3/src/config/options.ts
index 6a55861566..76906dfc89 100644
--- a/packages/nuxt3/src/config/options.ts
+++ b/packages/nuxt3/src/config/options.ts
@@ -442,6 +442,11 @@ function normalizeConfig (_options: CliConfiguration) {
 
   options._majorVersion = 3
 
+  if (options.vite && !options.dev) {
+    options.vite = false
+    consola.warn('Vite does not support production builds yet! Using webpack...')
+  }
+
   return options
 }
 
diff --git a/packages/nuxt3/src/webpack/index.ts b/packages/nuxt3/src/webpack/index.ts
deleted file mode 100644
index 80ab709001..0000000000
--- a/packages/nuxt3/src/webpack/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { WebpackBundler as BundleBuilder } from './builder'