At MyPartner ISC, we build ERPs — but not just one.
We build ERPs for schools, retail stores, logistics…
Same base idea, but every client wants it a bit different.
For a long time, we did what everyone does at first:
Copy. Paste. Pray.
It worked — until it didn’t.
- Fixing bugs? In 3 places.
- Adding features? Manually duplicated.
- Refactoring? Forget it.
So we stopped duplicating and built a setup where we could power multiple product UIs from a single codebase, with:
- microfrontends (but not the heavy kind),
- React Router for dynamic route loading,
- and a product config system that just works.
The big idea
We designed a simple setup:
- One monorepo for all shared ERP modules (dashboard, HR, finance, etc.)
- One host app per product (each ERP context)
- A central config that tells which product shows what
From copy-paste to config-driven
Here’s how we stopped duplicating UIs:
export const PRODUCTS_CONFIG = {
retailERP: {
name: "Retail ERP",
enabledModules: ["dashboard", "inventory", "sales"],
},
schoolERP: {
name: "School ERP",
enabledModules: ["dashboard", "students", "grades"],
},
};
Each host app like retailERP-host
uses its key (retailERP
) to know which modules to render.
⚙️ Modules export routes
Each module exports its own routes.
// modules/students/routes.ts export const studentsRoutes = [
{ path: "/students", element: <StudentList /> },
];`
In the host:
const productKey = "schoolERP"; const enabledModules = PRODUCTS_CONFIG[productKey].enabledModules; const MODULE_ROUTES = { dashboard: dashboardRoutes, inventory: inventoryRoutes, students: studentsRoutes, grades: gradesRoutes,
}; const routes = enabledModules.flatMap( (mod) => MODULE_ROUTES[mod] || []
);`
dynamic routes with no dead code.
🧪 Our flavor of microfrontends
We didn’t go full Module Federation. Instead:
-
Each module is isolated: UI, logic, routes, translations, etc.
-
Shared design system and internal framework
-
Hosts compose the UI by pulling modules + config
-
Everything lives in one single repo for modules
-
Each host has its own light repo (just to inject branding & build config)
So yeah — one repo to rule all ERP modules Hosts are tiny, controlled, and always in sync.
Why it was worth it
Before:
-
3 different ERPs = 3 different frontends
-
Same “Add Product” feature implemented 3x
Now:
-
One module → enabled in any product
-
Want to launch a new ERP? Just add a config + a new host repo
-
Cleaner rollouts, less maintenance
Optional toggles
Each module can check the product config at runtime:
`const { currentProduct } = useProductConfig(); if (currentProduct.name === "School ERP") { return <SchoolSpecificComponent />;
}`
Final thoughts
Not everyone needs complex microfrontend infra.
Sometimes, a single repo and smart configs are all you need.
And we’ll never go back to copy-paste again.