Development flags help you hide unfinished work. A good scenario where you'll use this will be something like this:
- You start using a new library for a new feature and setup a lot of config for it
- But now the team wants to start using this library immediately
- But you have an unfinished feature in your branch
- Your options are to create a new branch and redo that entire setup again
- Or hide the current unfinished work behind a development flag
- Raise a PR and the team can maybe also use that unfinished work as a reference on how to use this library
Install Mantine UI Library
So, the team decided that MUI is not the way to move forward and have agreed to use Mantine instead. The team wants you to first refresh the FeatureOne to use Mantine's Design.
Let us install it now:
pnpm add @mantine/core @mantine/hooksMantine needs postcss for styling, so let us install it too:
pnpm add -D postcss postcss-preset-mantine postcss-simple-varsAlso, create a postcss.config.cjs file for the configuration.
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
},
};We then setup Mantine's Provider:
import AuthLayout from '@/layouts/auth-layout';
import About from '@/pages/about';
import Home from '@/pages/home';
import Login from '@/pages/login';
import Register from '@/pages/register';
import { MantineProvider } from '@mantine/core';
import '@mantine/core/styles.css';
import { Route, Routes } from 'react-router';
import AppLayout from './layouts/app-layout';
function App() {
return (
<MantineProvider>
<Routes>
<Route element={<AppLayout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
</Route>
<Route element={<AuthLayout />}>
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
</Route>
</Routes>
</MantineProvider>
);
}
export default App;Refreshing Feature One's Design
Great, now we have the neccessary setup for Mantine, and we can start using it in our app. As discussed, let us start to refresh the FeatureOne design. We will replace the MUI's paper with Mantine's Paper in FeatureOne to begin with.
The current FeatureOne code looks like this:
import BuildIcon from '@mui/icons-material/Build';
import DescriptionIcon from '@mui/icons-material/Description';
import FolderIcon from '@mui/icons-material/Folder';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
Paper,
Typography,
} from '@mui/material';
export default function FeatureOne() {
return (
<Paper elevation={3} sx={{ p: 3, borderLeft: '5px solid #f44336' }}>
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</Paper>
);
}We start to duplicate the code to keep visual reference and start bringing in our Mantine Paper component.
import { Paper } from '@mantine/core';
import BuildIcon from '@mui/icons-material/Build';
import DescriptionIcon from '@mui/icons-material/Description';
import FolderIcon from '@mui/icons-material/Folder';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
Paper as MUIPaper,
Typography,
} from '@mui/material';
export default function FeatureOne() {
return (
<>
<Paper shadow="xs" withBorder p="xl">
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</Paper>
{/* USING THIS AS VISUAL REFERENCE TILL THE FULL MIGRATION IS COMPLETE */}
<MUIPaper elevation={3} sx={{ p: 3, borderLeft: '5px solid #f44336' }}>
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</MUIPaper>
</>
);
}Great, it looks like Mantine's paper works well. You decide to continue working on the other parts but now all of a sudden a high priority request has come in and the team needs to push a FeatureThree immediately due to a client request and they want it to use the new Mantine based UI.
useDevelopmentFlag and DevelopmentFlag
Now, all of your setup is in this branch and you don't want to redo this work just to create another branch. Again, assume you did a lot more work than what we just did above.
One quick solution that we have now is using a development flag to hide the unfinished work.
import { Paper } from '@mantine/core';
import BuildIcon from '@mui/icons-material/Build';
import DescriptionIcon from '@mui/icons-material/Description';
import FolderIcon from '@mui/icons-material/Folder';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
Paper as MUIPaper,
Typography,
} from '@mui/material';
function useDevelopmentFlag() {
return process.env.NODE_ENV === 'development';
}
export default function FeatureOne() {
const isDev = useDevelopmentFlag();
return (
<>
{isDev && (
<Paper shadow="xs" withBorder p="xl">
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and
layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</Paper>
)}
{/* USING THIS AS VISUAL REFERENCE TILL THE FULL MIGRATION IS COMPLETE */}
<MUIPaper elevation={3} sx={{ p: 3, borderLeft: '5px solid #f44336' }}>
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</MUIPaper>
</>
);
}That is it! This is the simplest of the flags we can implement. We can go one step ahead and create a wrapper component called DevelopmentFlag. Let us also separate out these into dedicated files.
export default function useDevelopmentFlag() {
return process.env.NODE_ENV === 'development';
}import useDevelopmentFlag from './use-development-flag';
export default function DevelopmentFlag({
children,
}: {
children: React.ReactNode;
}) {
const isDev = useDevelopmentFlag();
return isDev ? <>{children}</> : null;
}import { Paper } from '@mantine/core';
import BuildIcon from '@mui/icons-material/Build';
import DescriptionIcon from '@mui/icons-material/Description';
import FolderIcon from '@mui/icons-material/Folder';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
Paper as MUIPaper,
Typography,
} from '@mui/material';
import DevelopmentFlag from './development-flag';
export default function FeatureOne() {
return (
<>
<DevelopmentFlag>
<Paper shadow="xs" withBorder p="xl">
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and
layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</Paper>
</DevelopmentFlag>
{/* USING THIS AS VISUAL REFERENCE TILL THE FULL MIGRATION IS COMPLETE */}
<MUIPaper elevation={3} sx={{ p: 3, borderLeft: '5px solid #f44336' }}>
<Typography variant="h5" gutterBottom>
Old Feature One
</Typography>
<Typography variant="body1" gutterBottom>
This is the legacy version of Feature One with basic tools and layout.
</Typography>
<List>
<ListItem>
<ListItemIcon>
<FolderIcon color="action" />
</ListItemIcon>
<ListItemText primary="Basic data list" />
</ListItem>
<ListItem>
<ListItemIcon>
<BuildIcon color="action" />
</ListItemIcon>
<ListItemText primary="Minimal controls" />
</ListItem>
<ListItem>
<ListItemIcon>
<DescriptionIcon color="action" />
</ListItemIcon>
<ListItemText primary="Static content only" />
</ListItem>
</List>
</MUIPaper>
</>
);
}Now, this is the simplest example of a development flag. Though the usecase is quite limited, we wanted you to know you can do something like this.
In the next, section we will start using the core feature flags to release our FeatureThree component and in the further sections, our entire app and the layouts will be behind a feature flag.
At this point, your code should be a good match to the branch of the repository: 1-development-flags