FDK 10.1.5 quietly shipped one of the most impactful developer-facing features in recent times: support for a custom vite.config.js in your app project. I want to share what I did with it the moment I found out, because the results speak for themselves.
What changed
Before 10.1.5, the Vite configuration used to build your Freshworks app was entirely internal to FDK. You could observe it; you couldn’t touch it. If you wanted a different alias, an extra plugin, or a custom chunk strategy, you were out of luck.
Now, FDK looks for a vite.config.js at your project root, deep-merges it with its own internal config, and enforces only the handful of things it truly needs to own — entry points, the app/config resolve aliases, and its own plugins. Everything else is yours.
What I built with it: replacing React with Preact
My app (Checklists) ships a React 19 frontend. React’s runtime has always been the dominant cost in the bundle — there’s simply no way to tree-shake react-dom. It’s a floor you can’t dig below.
Preact is a 3 KB alternative with the same modern API. Its preact/compat layer is a drop-in compatibility shim that makes every React import — hooks, portals, forwardRef, Suspense, lazy, createRoot — work unchanged. The catch: wiring it up traditionally meant patching every import in the codebase. With FDK’s new vite.config.js support, the entire migration is four lines of aliases and zero component changes.
The config
// vite.config.js (placed at packages/app/ root)
import { defineConfig } from 'vite';
export default defineConfig({
resolve: {
alias: {
'react/jsx-runtime': 'preact/jsx-runtime',
'react/jsx-dev-runtime': 'preact/jsx-runtime',
'react-dom/client': 'preact/compat/client',
'react-dom/test-utils': 'preact/test-utils',
'react-dom': 'preact/compat',
'react': 'preact/compat',
},
},
});
When FDK picks it up, it logs:
FDK will merge your config with required settings. FDK plugins and aliases take precedence.
That’s it. Vite rewrites every import ... from 'react' — in your code and in every third-party dependency — to resolve through preact/compat before a single byte hits the bundler.
One note on react-dom/client
The alias for react-dom/client must point to preact/compat/client, not preact/compat. createRoot lives in the sub-path package; the top-level compat module doesn’t re-export it. Get this wrong and the build fails with:
"createRoot" is not exported by "node_modules/preact/compat/dist/compat.module.js"
The numbers
Measured on a production fdk pack build, uncompressed bytes:
| Chunk | React 19 | Preact/compat | Saved | Reduction |
|---|---|---|---|---|
config/assets/index.js (iparams page) |
1,182,830 B | 800,697 B | −382 KB | −32% |
app/assets/use-app-init.js (main runtime in ticket sidebar) |
497,661 B | 134,219 B | −363 KB | −73% |
app/assets/index.js |
146,692 B | 144,096 B | −2.6 KB | −2% |
app/assets/RichTextEditor.js (lazy loaded) |
210,853 B | 210,768 B | −85 B | — |
| Other chunks | ~367 KB | ~367 KB | — | — |
| Total JS | ~2.35 MB | ~1.60 MB | −745 KB | −32% |
The use-app-init chunk — the one that carries the entire React runtime — dropped 73%. The iparams config page, which bundles React separately, dropped 32%. Across the whole app, 745 KB of JavaScript is simply gone.
No components changed. No imports changed. No tests changed. The test suite passes with the same aliases wired into vitest.config.js.
Why this matters beyond bundle size
The compat alias technique is just one example of what custom Vite config unlocks. The same mechanism lets Freshworks app developers:
- Add
defineconstants for compile-time feature flags - Configure
server.proxyfor local API mocking duringfdk run - Supply
build.rollupOptions.output.manualChunksfor fine-grained code splitting - Register additional Vite plugins (PostCSS transforms, SVG loaders, etc.)
These are standard Vite capabilities that were previously inaccessible. FDK’s merge strategy is well-designed: it gives you the full power of the tool while protecting the parts it genuinely needs to own.
Thank you
Shipping this feature in FDK 10.1.5 closed a long-standing gap between “building for Freshworks” and “building with modern tooling.” The implementation is clean — the merge happens before FDK enforces its overrides, so user config and FDK config compose rather than fight.
To the Marketplace Platform team: this is exactly the kind of escape hatch that makes a platform feel like a tool, not a cage. Thank you to the entire team that made it happen.
@Santhosh , @Keerthiga_Ramasamy , @Siva_Venkatachalam , @Rohan_Kokkula
Regards,
Arun Rajkumar