~7 min read · Tutorial
I look at a lot of MUI admin templates. material-kit-react from the minimals people is one I keep going back to. Clean, typed, and the folder structure makes sense. Repo: , rendered src/main.tsx directly. No install, no Vite, no localhost. Below is what I did, including the bits that made me stop and think.
One honest note first. This does not replace your dev server. You still need the real thing for tests, prod builds, actual feature work. It's good for the look-and-tweak loop. Evaluating a template, recoloring something, showing a client. The stuff where booting the whole toolchain costs more than the task itself.
Quick note: local folder support requires a Pro account. To test it out, use the code in the original blog for a free upgrade. No credit card required, available while it lasts.
Your browser does not support video. Watch on
First time in, Studio sees there's no project setting file and pops a prompt:
This project has no CrossUI Studio setting file. We recommend auto-scanning the project to generate one first, then editing it by hand.
You can hand-edit it after. The Project Settings dialog opens right on the generated file. But the auto one was enough for everything below. Couple of seconds, still zero npm install.
3. Open src/main.tsx and hit preview
Close the Project Settings dialog and Studio opens the entry file for you. No need to go hunting in the file tree.
Here's the file you normally can't "just render":
// src/main.tsx
const router = createBrowserRouter([
{
Component: () => (
<App>
<Outlet />
</App>
),
errorElement: <ErrorBoundary />,
children: routesSection,
},
]);
createRoot(document.getElementById('root')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);
This is a Vite entry. createBrowserRouter + RouterProvider, and the whole app lives inside <App> (the theme provider) and routesSection (the routes). Render this file in isolation the naive way and it blows up. No router context, no theme, no #root the way the app wants it.
It rendered anyway. I configured nothing. Opened main.tsx, hit preview, and the dashboard showed up on the canvas with the MUI theme and all.
Ctrl+click on the canvas walks you DOWN this tree, one file at a time. The nice surprise: the provider wrapping follows you down by itself. Open the Render Decorations panel at any level and you can see the two wrappers listed, RouterProvider and ThemeProvider:
- On
main.tsxit lists both but auto-skipsRouterProvider. Makes sense, that provider lives in this very file, so wrapping again would double it. It only applies the theme. - On
sections.tsx, thendashboard.tsx, then below, both are on (the panel shows "2"). So router + theme context is there the whole way down. I never had to hand-fix a "missing provider" on the way.
That's usually the painful part of rendering a deep file in isolation, and it was handled for free. What was left on the way to the file I wanted were two small navigation detours. Neither is a crash. They're just what real code looks like.
Detour 1 — sections.tsx opens on the wrong JSX block (renderFallback).
Ctrl+click into the router file and it lands on renderFallback, the Suspense spinner:
// src/routes/sections.tsx
const renderFallback = () => (
<Box sx={{ display: 'flex', flex: '1 1 auto', alignItems: 'center', justifyContent: 'center' }}>
...
</Box>
);
So the canvas goes almost empty. One file can hold several JSX blocks and this spinner is just the first one. Up top there's a block navigator (the breadcrumb dropdown) listing them all. I jumped to routesSection and its children:
export const routesSection: RouteObject[] = [
{
element: ( /* <DashboardLayout><Suspense><Outlet/></Suspense></DashboardLayout> */ ),
children: [
{ index: true, element: <DashboardPage /> },
{ path: 'user', element: <UserPage /> },
{ path: 'products', element: <ProductsPage /> },
{ path: 'blog', element: <BlogPage /> },
],
},
// sign-in, 404 ...
];
Lesson: when a file has more than one JSX block, don't trust the first thing it shows. Use the block navigator to land on the piece you care about.
Detour 2 — index.ts is a barrel, not a component.
From DashboardPage I kept drilling and hit this:
// src/sections/overview/view/index.ts
export * from "./overview-analytics-view.tsx";
(The block navigator above the editor also jumps to any level directly, if clicking through gets old.)
5. Change something
overview-analytics-view.tsx is where the four stat cards get their props, and it reads easy:
<AnalyticsWidgetSummary
title="Weekly sales"
percent={2.6}
total={714000}
// color defaults to primary
/>
Two small edits, both from the canvas and the inspector. I changed the "Weekly sales" card's color to warning and pushed total up. Then flipped the "New users" card from secondary to success. The diff that lands is exactly that and nothing more:
<AnalyticsWidgetSummary
title="Weekly sales"
percent={2.6}
- total={714000}
+ total={928000}
+ color="warning"
...
/>
<AnalyticsWidgetSummary
title="New users"
percent={-0.1}
total={1352831}
- color="secondary"
+ color="success"
...
/>
That round trip, edit deep in a view then Back all the way out to see it in the whole app, is normally a reload plus a mental context switch. Here it's the same canvas and the Back button.
Total time from git clone to "amber card in the running dashboard": a couple of minutes, most of it the clone. Zero of it npm install.
Why the friction landed where it did (a note for template authors)
It wasn't quite zero friction. Two small navigation detours on the way down. But both were in predictable places, a multi-block router file and a barrel re-export, and each was a one-click fix. A good part of why it was that predictable is the template itself, not the tool. material-kit-react is put together in a way that cooperates:
- The entry (
main.tsx/app.tsx) is straightforward. Providers are right there, not hidden behind three layers of indirection. That's probably whyRouterProviderandThemeProvidergot picked up and stayed applied at every level I drilled into. Theme context just rode along, no manual provider fixing on the way down. - Routes are
lazy()and grouped inroutesSection, so the block navigator reads like the URL structure. SpottingDashboardPagenext toUserPage/ProductsPagetakes one glance.
sections/are split per feature behind short barrelindex.tsfiles. Keeps imports tidy in the real app, and when drilling you just click the oneexport *line. A predictable pattern beats a clever one.- The cards take plain props.
AnalyticsWidgetSummaryis justtitle/total/percent/color/chart, fed by the view. So editing one from the view is a data change, not a backend call, and the diff stays a single line.
A messier template would be harder to preview this way, tool or no tool. Providers assembled dynamically, one 2000-line page, sections that only work when fed real fetched data. So good template structure is a big part of what makes "open it without building it" realistic. Hats off to material-kit-react there, the structure is doing real work.
The practical upshot, if you make or sell templates. A chunk of the friction for someone evaluating your template is the clone-install-run tax before they see anything. Being previewable and tweakable straight from source cuts that tax a lot. Worth thinking about.
What I would not use it for
- Running the test suite or the real Vite build. This is preview + edit, not CI.
- Anything that needs live backend data to render at all. You can mock it for a drill-down, but that's a different workflow.
- A final answer on runtime behavior. It renders structure from the source. It's not watching your app actually execute end to end.
For "open this template, change a couple of things, see it in context", which is most of what I do with admin kits before committing to one, skipping the build was just less ceremony.
Quick note: local folder support requires a Pro account. To test it out, use the code in the original blog for a free upgrade. No credit card required, available while it lasts.
SOCIAL SHARE CARD GENERATOR