| |

Vue.js 78 🟢 Micro-Frontend Architectures with Vue and Module Federation

⏱️ Estimated read: 17 min | |

Micro-frontends apply the principle of service decomposition to the browser. Instead of one monolithic Vue application, the UI is split into independently developed, tested, and deployed fragments that a shell application composes at runtime. Module Federation is the mechanism that makes this composition practical: it allows a JavaScript application to expose modules to other applications and to consume modules from them, sharing dependencies like Vue itself so that multiple instances do not collide .

For Vue teams, Module Federation offers a path away from the “upgrade everything at once” problem. A legacy Vue 2 dashboard can coexist with a new Vue 3 feature module, each deployed on its own schedule, as long as the shell knows how to load both. This chapter examines how Module Federation works with Vue, how Vite-based configurations differ from Webpack’s original implementation, and what architectural rules keep federated Vue applications from becoming a distributed monolith.

Key point: Module Federation does not eliminate coupling; it relocates coupling from build time to runtime. The shared configuration—especially singleton Vue instances—is where that coupling is either managed successfully or exposed as a production bug.


Why Module Federation exists

The deployment coupling problem. In a traditional Vue SPA, every change to any route requires rebuilding and redeploying the entire application. A typo fix in the footer blocks a critical feature release. Module Federation breaks this coupling by allowing each exposed module to live in its own repository and deployment pipeline . The shell application fetches the module at runtime from a URL, meaning the remote team can ship updates without coordinating with the shell team. The trade-off is that runtime failures replace build-time failures: a remote that is down or misconfigured becomes a user-facing error rather than a compilation error.

The dependency duplication problem. Naively loading two independently built Vue applications into one page produces two Vue runtimes, two reactivity systems, and two sets of component instances. Context, provide/inject, and global state break across that boundary. Module Federation’s shared configuration exists to solve this: Vue is declared as a shared singleton, and the federation runtime negotiates which copy to use at load time . Without this, federated Vue applications cannot communicate through normal Vue mechanisms.

The framework lock-in problem. Webpack 5 introduced Module Federation, which meant teams wanting to use it were tied to Webpack’s build pipeline . Vite’s ecosystem has produced several federation plugins, most notably @module-federation/vite and @originjs/vite-plugin-federation, which bring the same concepts to Vite’s ESM-based dev server and Rollup production builds . The rise of Module Federation 2.0 added a framework-agnostic runtime, a manifest format, and dynamic type hinting, making federation less dependent on a specific bundler .

The team autonomy problem. Micro-frontends are as much an organizational pattern as a technical one. When a single team owns the entire frontend, the overhead of federation rarely justifies itself. When multiple teams own distinct business domains—checkout, catalog, account—federation allows those teams to choose their own release cadence, their own testing strategy, and even their own Vue version if necessary . The cost is coordination: shared design tokens, consistent authentication context, and a contract for what each remote exposes must be governed deliberately.


a. Module Federation Configuration for Vue

A Module Federation setup has two sides. The remote application exposes modules through an entry file. The host (or shell) application declares where to find those remotes and consumes their exposed modules. Both sides declare which dependencies should be shared.

Using @module-federation/vite, a Vue remote’s configuration looks like this:

// remote-app/vite.config.ts — exposes a Vue component to other applications
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { federation } from '@module-federation/vite'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      // name is the global identifier the host uses to reference this remote
      name: 'remote',
      // filename is the generated entry file the host downloads
      filename: 'remoteEntry.js',
      // exposes maps import paths to actual files in this project
      exposes: {
        './ProductCard': './src/components/ProductCard.vue'
      },
      // shared ensures Vue is not duplicated across host and remote
      shared: ['vue']
    })
  ]
})

The host configuration declares the remote’s URL and shared dependencies. With @module-federation/vite, the remote entry can be referenced as a module URL.

// host-app/vite.config.ts — consumes the remote Vue component
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { federation } from '@module-federation/vite'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      name: 'host',
      // remotes maps a local alias to the remote's entry file URL
      remotes: {
        remote: {
          type: 'module',
          name: 'remote',
          entry: 'http://localhost:5001/remoteEntry.js',
          entryGlobalName: 'remote',
          shareScope: 'default'
        }
      },
      // Vue must be shared here as well, or the remote loads its own copy
      shared: ['vue']
    })
  ]
})

Consuming the remote component in a Vue host uses defineAsyncComponent, because the remote module is loaded over the network and cannot be resolved synchronously .

<!-- host-app/src/App.vue — loads the remote component asynchronously -->
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

// The import path matches remoteName/exposedKey from the remote's config
const ProductCard = defineAsyncComponent(
  () => import('remote/ProductCard')
)
</script>

<template>
  <div>
    <h1>Host Application</h1>
    <!-- Render the remote component; it behaves like any local component -->
    <ProductCard v-if="ProductCard" />
  </div>
</template>

The shared: ['vue'] shorthand is convenient but insufficient for production Vue federation. Vue must be a singleton with a requiredVersion, or host and remote will silently run different Vue versions .

// Production shared configuration — Vue must be a singleton
shared: {
  vue: {
    // singleton: true forces one Vue instance across all federated apps
    singleton: true,
    // requiredVersion makes incompatible versions fail fast rather than silently
    requiredVersion: '^3.5.0'
  }
}

b. Remote Modules, Routing, and Application Boundaries

Exposing a single component is the simplest case. Real micro-frontends usually expose route-level features—a complete page or section with its own internal routing, state, and API calls. This introduces boundary questions that component-level federation does not face.

A remote application built with Vue Router can expose its route configuration rather than individual components. The host imports the routes and merges them into its own router.

// remote-app/src/routes.ts — exported route definitions for host consumption
import type { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    // The host will mount these routes under its own path prefix
    path: 'products',
    name: 'products',
    component: () => import('./views/ProductList.vue'),
    children: [
      {
        path: ':id',
        name: 'product-detail',
        component: () => import('./views/ProductDetail.vue')
      }
    ]
  }
]

export default routes
// remote-app/vite.config.ts — exposes routes, not just components
exposes: {
  './routes': './src/routes.ts',
  './ProductCard': './src/components/ProductCard.vue'
}

The host consumes the routes and adds them to its router. This pattern requires that the remote’s internal routes use relative paths (without a leading slash), so the host can mount them under any prefix.

// host-app/src/router/index.ts — merges remote routes into the host router
import { createRouter, createWebHistory } from 'vue-router'
import remoteRoutes from 'remote/routes'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: () => import('../views/Home.vue') },
    // Mount remote routes under the /shop prefix
    {
      path: '/shop',
      component: () => import('../layouts/ShopLayout.vue'),
      children: remoteRoutes
    }
  ]
})

export default router

This approach keeps the remote in control of its internal navigation while the host controls where that feature lives in the overall URL space. The critical rule is that the remote must not assume it owns the root path. A remote route with path: '/' will conflict with the host’s own root route and produce silent navigation failures .

Shared state across the boundary is another decision point. Vue’s provide/inject works across federated components only if Vue is a true singleton. If both applications run the same Vue instance, a provide in the host is visible to injected dependencies in the remote . If the singleton configuration is wrong, inject returns undefined and the remote fails in ways that are difficult to debug.


c. Operational Rules and Module Federation 2.0

Module Federation’s runtime nature means that configuration correctness is not enough. The operational model—how remotes are versioned, how failures are handled, and how the shared scope is governed—determines whether federation is sustainable.

Manifest-based loading. Module Federation 2.0 introduced a manifest format that describes a remote’s exposes, shared dependencies, and metadata as JSON . Instead of hardcoding a remoteEntry.js URL, the host fetches the manifest first, then loads the specific remote entry based on that manifest. This enables version resolution, integrity checking, and fallback URLs.

// Host consuming a manifest URL instead of a raw entry file
remotes: {
  catalog: 'https://cdn.example.com/catalog/mf-manifest.json'
}

Error isolation. A remote that fails to load must not take down the host. Vue’s defineAsyncComponent accepts errorComponent and loadingComponent options, which provide a boundary around the remote.

<script setup lang="ts">
import { defineAsyncComponent, h } from 'vue'

const ProductCard = defineAsyncComponent({
  loader: () => import('remote/ProductCard'),
  // Show a fallback while the remote loads
  loadingComponent: () => h('div', 'Loading...'),
  // Show this if the remote fails to load
  errorComponent: () => h('div', 'Product unavailable'),
  // Wait 2 seconds before showing the loading state
  delay: 200
})
</script>

Version negotiation. The shareScope and requiredVersion settings on shared dependencies determine which copy of Vue wins when multiple remotes declare different requirements. By default, the highest compatible version is used. Setting singleton: true with strictVersion: true forces a build-time error if an incompatible version is encountered, which surfaces the problem before deployment rather than at runtime .

The shell’s responsibility. The host application owns the shared context: authentication state, theme tokens, and the Vue runtime itself. Remotes should treat these as contracts, not implementation details. If a remote needs to know the current user, that state should be provided by the shell through a documented mechanism (provide/inject or a shared store), not fetched independently by each remote . This keeps the number of network requests proportional to the page, not to the number of federated features.

Type sharing. Module Federation 2.0’s dynamic type hinting generates TypeScript declarations for exposed modules, so the host gets autocomplete and type checking for remote imports without a manual publishing step . This is a significant improvement over earlier setups, where remote module types had to be maintained separately or omitted entirely.


Complete Example Session

// ============================================
// PART 1: PROJECT STRUCTURE
// ============================================
// A host Vue application consuming a remote Vue feature
//
// host-app/
// ├── src/
// │   ├── App.vue
// │   ├── router/index.ts
// │   └── main.ts
// └── vite.config.ts
//
// remote-app/
// ├── src/
// │   ├── routes.ts
// │   ├── views/ProductList.vue
// │   └── views/ProductDetail.vue
// └── vite.config.ts


// ============================================
// PART 2: REMOTE VITE CONFIGURATION
// ============================================
// remote-app/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { federation } from '@module-federation/vite'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      name: 'catalog',
      filename: 'remoteEntry.js',
      // Expose route definitions and a reusable component
      exposes: {
        './routes': './src/routes.ts',
        './ProductCard': './src/components/ProductCard.vue'
      },
      // Vue must be a singleton to share reactivity and provide/inject
      shared: {
        vue: { singleton: true, requiredVersion: '^3.5.0' }
      }
    })
  ],
  // Module Federation requires ESM-compatible build target
  build: { target: 'chrome89' }
})


// ============================================
// PART 3: REMOTE ROUTE DEFINITIONS
// ============================================
// remote-app/src/routes.ts — relative paths so host can mount under any prefix
import type { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    path: '',  // Empty path renders ProductList at the mount point
    name: 'product-list',
    component: () => import('./views/ProductList.vue')
  },
  {
    path: ':id',
    name: 'product-detail',
    component: () => import('./views/ProductDetail.vue')
  }
]

export default routes


// ============================================
// PART 4: HOST VITE CONFIGURATION
// ============================================
// host-app/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { federation } from '@module-federation/vite'

export default defineConfig({
  plugins: [
    vue(),
    federation({
      name: 'shell',
      remotes: {
        // Alias "catalog" maps to the remote's entry file
        catalog: {
          type: 'module',
          name: 'catalog',
          entry: 'http://localhost:5001/remoteEntry.js',
          entryGlobalName: 'catalog',
          shareScope: 'default'
        }
      },
      // Host must also share Vue as singleton
      shared: {
        vue: { singleton: true, requiredVersion: '^3.5.0' }
      }
    })
  ],
  build: { target: 'chrome89' }
})


// ============================================
// PART 5: HOST ROUTER WITH REMOTE ROUTES
// ============================================
// host-app/src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
// Import remote routes — resolved through Module Federation at runtime
import remoteRoutes from 'catalog/routes'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: () => import('../views/Home.vue')
    },
    {
      // Mount remote routes under /catalog
      // The remote's empty path renders at /catalog
      path: '/catalog',
      children: remoteRoutes
    }
  ]
})

export default router


// ============================================
// PART 6: HOST CONSUMING REMOTE COMPONENT
// ============================================
// host-app/src/views/Home.vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

// Async component with error boundary
const ProductCard = defineAsyncComponent({
  loader: () => import('catalog/ProductCard'),
  // Fallback UI if remote fails to load
  errorComponent: () => h('div', { class: 'error' }, 'Product unavailable'),
  delay: 200
})
</script>

<template>
  <div>
    <h1>Shell Home</h1>
    <ProductCard v-if="ProductCard" />
  </div>
</template>


// ============================================
// PART 7: REMOTE COMPONENT
// ============================================
// remote-app/src/components/ProductCard.vue
<script setup lang="ts">
// This component can inject dependencies provided by the host
// because Vue is a shared singleton
const props = defineProps<{ productId: string }>()
</script>

<template>
  <div class="product-card">
    <p>Product: {{ productId }}</p>
  </div>
</template>


// ============================================
// PART 8: HOST PROVIDING SHARED CONTEXT
// ============================================
// host-app/src/App.vue — provides auth state to all federated children
<script setup lang="ts">
import { provide, ref } from 'vue'

const currentUser = ref({ id: '1', name: 'Alice' })
// Any remote component that calls inject('currentUser') receives this ref
provide('currentUser', currentUser)
</script>

<template>
  <RouterView />
</template>


// ============================================
// PART 9: REMOTE INJECTING HOST CONTEXT
// ============================================
// remote-app/src/views/ProductDetail.vue
<script setup lang="ts">
import { inject } from 'vue'

// This works only if Vue is a true singleton across host and remote
const currentUser = inject<Ref<{ name: string }>>('currentUser')
</script>

<template>
  <div>
    <p v-if="currentUser">Viewed by: {{ currentUser.name }}</p>
  </div>
</template>


// ============================================
// PART 10: PRODUCTION SHARED CONFIGURATION
// ============================================
// Both host and remote must match this configuration
shared: {
  vue: {
    singleton: true,
    // strictVersion prevents silent version mismatches
    strictVersion: true,
    requiredVersion: '^3.5.0'
  }
}

The ten parts trace the full federation path: the remote exposes routes and components; the host declares the remote URL and mounts the routes; shared Vue singleton enables provide/inject across the boundary; and production configuration enforces version compatibility.


Quick Reference

Federation Configuration Options

OptionSidePurpose
nameBothGlobal identifier for the federation container
filenameRemoteGenerated entry file name (default remoteEntry.js)
exposesRemoteMap of public module paths to local files
remotesHostMap of aliases to remote entry URLs
sharedBothDependencies negotiated at runtime
singletonBothForce one instance across all federated apps

Shared Vue Configuration

SettingRecommendedEffect
singletontrueOne Vue runtime; provide/inject works
requiredVersionProject’s Vue rangeFails if remote’s Vue is incompatible
strictVersiontrue in productionBuild-time error instead of silent mismatch
importfalse for optional depsExclude dependency from bundle if unused

Remote Route Rules

RuleReason
Use relative paths (no leading /)Host controls mount prefix
Empty path: '' for index routeRenders at mount point
Export routes as a typed arrayHost router merges them directly
Avoid root path: '/'Conflicts with host root route

Failure Handling

ScenarioMitigation
Remote URL is downerrorComponent in defineAsyncComponent
Remote loads slowlyloadingComponent with delay
Version mismatchstrictVersion: true with requiredVersion
Remote route conflictsEnforce relative-path convention

Best Practices

✅ Do This:

// Declare Vue as singleton with requiredVersion on both sides
vue: { singleton: true, requiredVersion: '^3.5.0' }          // ✅
<!-- Wrap remote imports in defineAsyncComponent with error handling -->
defineAsyncComponent({ loader: () => import('remote/App') }) // ✅
// Use relative paths in remote route definitions
path: 'products'                                             // ✅
// Provide shared context from the shell via provide/inject
provide('auth', currentUser)                                 // ✅
// Expose routes as data, not as a configured router instance
export default routes                                        // ✅
// Set build target to chrome89 or later for federation
build: { target: 'chrome89' }                                // ✅

❌ Don’t Do This:

// Share Vue without singleton configuration
shared: ['vue']                                              // ❌
<!-- Import remote modules synchronously in setup -->
import RemoteApp from 'remote/App'                           // ❌
// Use absolute paths in remote routes
path: '/products'                                            // ❌
// Fetch user state independently in each remote
const user = await fetchUser()                               // ❌
// Export a configured router instance instead of route array
export default createRouter({ routes })                      // ❌
// Target older browsers without the top-level await plugin
build: { target: 'es2015' }                                  // ❌

Common Pitfalls

PitfallWhy It HappensFix
inject returns undefined in remoteVue not a singletonSet singleton: true on both sides
Remote component renders blankMissing defineAsyncComponentWrap remote import in defineAsyncComponent
Two Vue instances in devtoolsshared omitted on one sideBoth host and remote must declare shared
Remote route not matchedAbsolute path in remote configRemove leading slash from remote path
Build fails on old browser targetTop-level await unsupportedSet build.target: 'chrome89' or add plugin
Remote updates not appearingBrowser caching remoteEntry.jsAdd cache-busting to remote URL or use manifest

Real-World Examples

1. Exposing a Vue Component

exposes: {
  './Button': './src/components/Button.vue'
}

2. Host Loading Remote Route Array

import remoteRoutes from 'remote/routes'
router.addRoute({ path: '/feature', children: remoteRoutes })

3. Singleton Vue with Version Check

shared: {
  vue: { singleton: true, requiredVersion: '^3.5.0' }
}

4. Error Boundary for Remote

const Remote = defineAsyncComponent({
  loader: () => import('remote/App'),
  errorComponent: () => h('div', 'Unavailable')
})

5. Remote Injecting Host Auth

const user = inject('currentUser')
// Works only with singleton Vue

6. Manifest-Based Remote Loading

remotes: {
  catalog: 'https://cdn.example.com/catalog/mf-manifest.json'
}

7. Vite Remote Build Target

build: { target: 'chrome89' }

8. Sharing a State Library

shared: {
  vue: { singleton: true },
  pinia: { singleton: true }
}

9. Exposing a Composable

exposes: {
  './useCart': './src/composables/useCart.ts'
}

10. Host Router Mounting Remote

{
  path: '/catalog',
  children: remoteCatalogRoutes
}

Visual

Host and Remote Architecture

┌──────────────────────────────────────────────────────────────┐
│  HOST APPLICATION                                            │
│                                                              │
│  ┌─────────────────┐      ┌─────────────────┐               │
│  │ Local routes    │      │ Remote routes   │               │
│  │ /               │      │ /catalog/*      │               │
│  │ /about          │      │                 │               │
│  └────────┬────────┘      └────────┬────────┘               │
│           │                        │                         │
│           ▼                        ▼                         │
│  ┌─────────────────┐      ┌─────────────────┐               │
│  │ Local Vue       │      │ Remote Vue      │               │
│  │ components      │      │ components      │               │
│  └─────────────────┘      └────────┬────────┘               │
│                                    │                         │
│                                    │ runtime fetch           │
│                                    ▼                         │
│                           ┌─────────────────┐               │
│                           │ remoteEntry.js  │               │
│                           │ (remote server) │               │
│                           └─────────────────┘               │
│                                                              │
│  Vue is shared as a singleton across both boundaries.        │
│  provide/inject works because there is one Vue instance.     │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Shared Dependency Resolution

┌──────────────────────────────────────────────────────────────┐
│  FEDERATION RUNTIME SHARED SCOPE                             │
│                                                              │
│  Host declares:              Remote declares:                │
│  vue: { singleton: true,     vue: { singleton: true,        │
│         requiredVersion:              requiredVersion:       │
│         '^3.5.0' }                    '^3.5.0' }             │
│           │                              │                   │
│           └──────────────┬───────────────┘                   │
│                          ▼                                   │
│              ┌─────────────────────┐                        │
│              │ Runtime negotiates  │                        │
│              │ one Vue instance    │                        │
│              └──────────┬──────────┘                        │
│                         │                                    │
│                         ▼                                    │
│              ┌─────────────────────┐                        │
│              │ Single Vue runtime  │                        │
│              │ shared by both apps │                        │
│              └─────────────────────┘                        │
│                                                              │
│  Without singleton, each app loads its own Vue copy.         │
│  provide/inject across the boundary returns undefined.       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Remote Route Mounting

┌──────────────────────────────────────────────────────────────┐
│  HOST ROUTER                                                 │
│                                                              │
│  routes: [                                                   │
│    { path: '/',           component: Home },                 │
│    { path: '/catalog',                                       │
│      children: [                                             │
│        { path: '',      component: ProductList },  ← /catalog│
│        { path: ':id',   component: ProductDetail } ← /catalog/1│
│      ]                                                       │
│    }                                                         │
│  ]                                                           │
│                                                              │
│  Remote routes use RELATIVE paths.                           │
│  The host prefixes them with /catalog.                       │
│  If remote used path: '/', it would match the host's root.   │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Failure Isolation Flow

┌──────────────────────────────────────────────────────────────┐
│  HOST RENDERS PAGE                                           │
│                                                              │
│  ┌─────────────────┐                                        │
│  │ defineAsync     │                                        │
│  │ Component       │                                        │
│  │ starts loading  │                                        │
│  └────────┬────────┘                                        │
│           ▼                                                  │
│  ┌─────────────────┐     ┌─────────────────┐               │
│  │ Remote loads?   │────►│ YES: Render     │               │
│  │                 │     │ remote component│               │
│  └────────┬────────┘     └─────────────────┘               │
│           │ NO (timeout, 404, error)                        │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ errorComponent  │                                        │
│  │ renders         │                                        │
│  └─────────────────┘                                        │
│                                                              │
│  The host page remains functional.                           │
│  Only the remote feature is unavailable.                     │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
RemoteApplication that exposes modules via exposes
HostApplication that consumes remotes via remotes
remoteEntry.jsGenerated entry file the host downloads
sharedDependencies negotiated at runtime
singleton: trueOne instance across all federated apps
Vue singletonRequired for provide/inject across boundaries
defineAsyncComponentWraps remote imports with loading/error states
Relative route pathsAllow host to control mount prefix
Module Federation 2.0Adds manifest, runtime, dynamic types
@module-federation/viteVite plugin for federation with Vue

Key takeaways:

  • Vue must be a shared singleton. Without singleton: true and matching requiredVersion, the host and remote each load their own Vue runtime. Reactivity, provide/inject, and global state break across that boundary, and the failure mode is often silent.
  • Remote routes use relative paths. The host owns the URL structure. A remote that declares path: '/' will conflict with the host’s root route. Exporting route arrays instead of configured router instances keeps the remote reusable under any prefix.
  • Wrap remote imports in defineAsyncComponent. Remote modules are network resources. They can be slow, unavailable, or broken. The host must provide loading and error states so that one failed remote does not blank the entire page.
  • The shell provides shared context. Authentication, theme, and other cross-cutting state should flow from host to remote through provide/inject or a shared store. Each remote fetching user data independently multiplies network requests and creates inconsistency.
  • Module Federation 2.0 improves the operational model. Manifest-based loading, dynamic type hinting, and a framework-agnostic runtime address many of the rough edges in Webpack 5’s original implementation. The manifest format enables version resolution and fallback URLs that raw remoteEntry.js URLs cannot express.

Remember: Module Federation moves the integration point from build time to runtime. That shift enables independent deployment, but it also means failures happen in the browser rather than in CI. The configuration that matters most is not exposes or remotes—it is shared. Vue as a singleton is the foundation. Without it, federated Vue applications are not really one application; they are two applications that happen to share a page. With it, they share reactivity, context, and the component model itself, which is what makes the composition feel native rather than bolted together.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!