Opinionated frontend development standards for modern React + TypeScript applications. Covers Suspense-first data fetching, lazy loading, feature-based architecture, MUI v7 styling, TanStack Router, performance optimization, and strict TypeScript practices.
67
67%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Frequently used patterns for forms, authentication, DataGrid, dialogs, and other common UI elements.
import { useAuth } from '@/hooks/useAuth';
export const MyComponent: React.FC = () => {
const { user } = useAuth();
// Available properties:
// - user.id: string
// - user.email: string
// - user.username: string
// - user.roles: string[]
return (
<div>
<p>Logged in as: {user.email}</p>
<p>Username: {user.username}</p>
<p>Roles: {user.roles.join(', ')}</p>
</div>
);
};NEVER make direct API calls for auth - always use useAuth hook.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { TextField, Button } from '@mui/material';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
// Zod schema for validation
const formSchema = z.object({
username: z.string().min(3, 'Username must be at least 3 characters'),
email: z.string().email('Invalid email address'),
age: z.number().min(18, 'Must be 18 or older'),
});
type FormData = z.infer<typeof formSchema>;
export const MyForm: React.FC = () => {
const { showSuccess, showError } = useMuiSnackbar();
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
username: '',
email: '',
age: 18,
},
});
const onSubmit = async (data: FormData) => {
try {
await api.submitForm(data);
showSuccess('Form submitted successfully');
} catch (error) {
showError('Failed to submit form');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
{...register('username')}
label='Username'
error={!!errors.username}
helperText={errors.username?.message}
/>
<TextField
{...register('email')}
label='Email'
error={!!errors.email}
helperText={errors.email?.message}
type='email'
/>
<TextField
{...register('age', { valueAsNumber: true })}
label='Age'
error={!!errors.age}
helperText={errors.age?.message}
type='number'
/>
<Button type='submit' variant='contained'>
Submit
</Button>
</form>
);
};From BEST_PRACTICES.md - All dialogs should have:
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton } from '@mui/material';
import { Close, Info } from '@mui/icons-material';
interface MyDialogProps {
open: boolean;
onClose: () => void;
onConfirm: () => void;
}
export const MyDialog: React.FC<MyDialogProps> = ({ open, onClose, onConfirm }) => {
return (
<Dialog open={open} onClose={onClose} maxWidth='sm' fullWidth>
<DialogTitle>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Info color='primary' />
Dialog Title
</Box>
<IconButton onClick={onClose} size='small'>
<Close />
</IconButton>
</Box>
</DialogTitle>
<DialogContent>
{/* Content here */}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onConfirm} variant='contained'>
Confirm
</Button>
</DialogActions>
</Dialog>
);
};From BEST_PRACTICES.md - DataGrid wrappers should accept:
Required Props:
rows: Data arraycolumns: Column definitionsOptional Props:
import { DataGridPro } from '@mui/x-data-grid-pro';
import type { GridColDef } from '@mui/x-data-grid-pro';
interface DataGridWrapperProps {
rows: any[];
columns: GridColDef[];
loading?: boolean;
toolbar?: React.ReactNode;
onRowClick?: (row: any) => void;
}
export const DataGridWrapper: React.FC<DataGridWrapperProps> = ({
rows,
columns,
loading = false,
toolbar,
onRowClick,
}) => {
return (
<DataGridPro
rows={rows}
columns={columns}
loading={loading}
slots={{ toolbar: toolbar ? () => toolbar : undefined }}
onRowClick={(params) => onRowClick?.(params.row)}
// Standard configuration
pagination
pageSizeOptions={[25, 50, 100]}
initialState={{
pagination: { paginationModel: { pageSize: 25 } },
}}
/>
);
};import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
export const useUpdateEntity = () => {
const queryClient = useQueryClient();
const { showSuccess, showError } = useMuiSnackbar();
return useMutation({
mutationFn: ({ id, data }: { id: number; data: any }) =>
api.updateEntity(id, data),
onSuccess: (result, variables) => {
// Invalidate affected queries
queryClient.invalidateQueries({ queryKey: ['entity', variables.id] });
queryClient.invalidateQueries({ queryKey: ['entities'] });
showSuccess('Entity updated');
},
onError: () => {
showError('Failed to update entity');
},
});
};
// Usage
const updateEntity = useUpdateEntity();
const handleSave = () => {
updateEntity.mutate({ id: 123, data: { name: 'New Name' } });
};Use TanStack Query for all server data:
// ✅ CORRECT - TanStack Query for server data
const { data: users } = useSuspenseQuery({
queryKey: ['users'],
queryFn: () => userApi.getUsers(),
});Use useState for local UI state only:
// ✅ CORRECT - useState for UI state
const [modalOpen, setModalOpen] = useState(false);
const [selectedTab, setSelectedTab] = useState(0);Use Zustand only for global client state:
import { create } from 'zustand';
interface AppState {
sidebarOpen: boolean;
toggleSidebar: () => void;
}
export const useAppState = create<AppState>((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
}));Avoid prop drilling - use context or Zustand instead.
Common Patterns:
See Also: