Skip to content

Development Best Practices

Master advanced React development patterns, performance optimization techniques, and enterprise-level code organization strategies used in the React admin template.

React 19 TypeScript Strict Performance Enterprise Ready

The template uses strict TypeScript configuration for maximum type safety and code quality:

tsconfig.app.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
/* Strict type-checking options */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"erasableSyntaxOnly": true,
/* Module resolution */
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src", "globals.d.ts"]
}

Define comprehensive interfaces for all data structures:

src/types/index.ts
// API Response patterns
export interface ApiResponse<T> {
data: T
message?: string
success: boolean
code?: number
}
// Pagination with strict typing
export interface PaginatedResponse<T> {
data: T[]
total: number
page: number
limit: number
totalPages: number
}
// User entity with role-based typing
export interface User {
id: string
name: string
email: string
avatar?: string
role: Role
status: Status
createdAt: string
updatedAt: string
}
// Component props with strict structure
export interface BaseComponentProps {
className?: string
children?: React.ReactNode
}
// Navigation structure typing
export interface NavItem {
title: string
url: string
icon?: React.ComponentType
isActive?: boolean
children?: NavItem[]
}

The template implements optimized custom hooks with proper dependency management:

src/hooks/useMediaQuery.ts
import { useState, useEffect, useMemo } from "react"
/**
* Optimized media query hook with memoized values
*/
export function useMediaQuery() {
// Core media query states
const isMobile = useMediaQueryCore('(max-width: 767px)')
const isTablet = useMediaQueryCore('(min-width: 768px) and (max-width: 1279px)')
const isDesktop = useMediaQueryCore('(min-width: 1280px)')
// Memoized computed values for performance
const canShowThreeColumns = useMemo(() => isDesktop, [isDesktop])
const shouldUseFullScreen = useMemo(() => isMobile, [isMobile])
const canShowTwoColumns = useMemo(() =>
isTablet || isDesktop, [isTablet, isDesktop]
)
return {
isMobile,
isTablet,
isDesktop,
// Layout helpers
canShowThreeColumns,
shouldUseFullScreen,
canShowTwoColumns,
}
}
function useMediaQueryCore(query: string): boolean {
const [matches, setMatches] = useState<boolean>(false)
useEffect(() => {
const mediaQuery = window.matchMedia(query)
setMatches(mediaQuery.matches)
const handleChange = (event: MediaQueryListEvent) => {
setMatches(event.matches)
}
mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange)
}, [query])
return matches
}

Robust localStorage hook with error handling and type safety:

src/hooks/useLocalStorage.ts
import { useState } from 'react'
export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
// Lazy initial state to avoid reading localStorage on every render
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error)
return initialValue
}
})
// Wrapper function to persist updates to localStorage
const setValue = (value: T | ((val: T) => T)) => {
try {
// Support function updates like useState
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error)
}
}
return [storedValue, setValue]
}

Complex table hook with validation and memoization:

src/components/HiTable/hooks/useTable.ts
import { useEffect, useMemo, useState, useCallback, useRef } from 'react'
import { useReactTable, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, getExpandedRowModel } from '@tanstack/react-table'
import type { ColumnFiltersState, SortingState, VisibilityState, ExpandedState } from '@tanstack/react-table'
// Comprehensive validation system
const validateTableData = <T>(data: T[], options: TableValidationOptions = {}): ValidationResult => {
const errors: string[] = []
const warnings: string[] = []
if (!Array.isArray(data)) {
errors.push('Data must be an array')
return { isValid: false, errors, warnings }
}
if (data.length === 0) {
warnings.push('No data provided')
}
if (options.strictMode) {
// Check for consistent data structure
if (data.length > 0) {
const firstRowKeys = Object.keys(data[0] || {})
const inconsistentRows = data.slice(1).filter(row => {
const rowKeys = Object.keys(row || {})
return rowKeys.length !== firstRowKeys.length ||
!firstRowKeys.every(key => key in (row || {}))
})
if (inconsistentRows.length > 0) {
warnings.push(`${inconsistentRows.length} rows have inconsistent structure`)
}
}
}
return { isValid: errors.length === 0, errors, warnings }
}
const validateColumns = <T = any>(columns: ColumnDef<T, unknown>[], options: TableValidationOptions = {}): ValidationResult => {
const errors: string[] = []
const warnings: string[] = []
if (!Array.isArray(columns) || columns.length === 0) {
errors.push('At least one column must be defined')
return { isValid: false, errors, warnings }
}
// Check for duplicate column IDs
const columnIds = columns.map(col => {
if ('id' in col && col.id) return col.id
if ('accessorKey' in col && col.accessorKey) return String(col.accessorKey)
return undefined
}).filter(Boolean)
const duplicates = columnIds.filter((id, index) => columnIds.indexOf(id) !== index)
if (duplicates.length > 0) {
errors.push(`Duplicate column IDs found: ${[...new Set(duplicates)].join(', ')}`)
}
return { isValid: errors.length === 0, errors, warnings }
}
export function useTableInstance<T = any>(
props: HiTableProps<T>,
config: TableConfig = {},
validationOptions: TableValidationOptions = {}
) {
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
const [rowSelection, setRowSelection] = useState({})
const [expanded, setExpanded] = useState<ExpandedState>({})
const [validationErrors, setValidationErrors] = useState<string[]>([])
// Performance: Use stable dependencies for memoization
const dataLength = props.data.length
const columnsLength = props.columns.length
const isStrictMode = validationOptions.strictMode
// Memoized validation to prevent unnecessary recalculations
const validation = useMemo(() => {
const dataValidation = validateTableData(props.data, validationOptions)
const columnValidation = validateColumns(props.columns, validationOptions)
return {
data: dataValidation,
columns: columnValidation,
isValid: dataValidation.isValid && columnValidation.isValid,
allErrors: [...dataValidation.errors, ...columnValidation.errors],
allWarnings: [...dataValidation.warnings, ...columnValidation.warnings]
}
}, [props.data, props.columns, dataLength, columnsLength, isStrictMode])
// Update validation errors when validation changes
useEffect(() => {
setValidationErrors(validation.allErrors)
if (validation.allWarnings.length > 0 && process.env.NODE_ENV === 'development') {
console.warn('HiTable validation warnings:', validation.allWarnings)
}
}, [validation.allErrors, validation.allWarnings])
// Default configuration with comprehensive options
const defaultConfig: TableConfig = useMemo(() => ({
pagination: { enabled: true, pageSize: 10, showPageInfo: true },
sorting: { enabled: true, multiSort: false },
filtering: { enabled: true, searchable: true },
columnVisibility: { enabled: true, buttonText: 'Columns' },
rowSelection: { enabled: false },
expandable: { enabled: false },
styling: { compact: false },
emptyState: { message: 'No results found.' },
loading: { enabled: false, message: 'Loading...' },
}), [])
// Memoized configuration object
const mergedConfig = useMemo(() => ({
...defaultConfig,
...config,
pagination: { ...defaultConfig.pagination, ...config.pagination },
sorting: { ...defaultConfig.sorting, ...config.sorting },
filtering: { ...defaultConfig.filtering, ...config.filtering },
columnVisibility: { ...defaultConfig.columnVisibility, ...config.columnVisibility },
rowSelection: { ...defaultConfig.rowSelection, ...config.rowSelection },
expandable: { ...defaultConfig.expandable, ...config.expandable },
styling: { ...defaultConfig.styling, ...config.styling },
emptyState: { ...defaultConfig.emptyState, ...config.emptyState },
loading: { ...defaultConfig.loading, ...config.loading },
}), [config, defaultConfig])
// Stable table configuration
const tableConfig = useMemo(() => ({
data: validation.isValid ? props.data : [],
columns: validation.isValid ? props.columns : [],
getCoreRowModel: getCoreRowModel(),
// Conditional feature enabling
...(mergedConfig.pagination?.enabled && {
getPaginationRowModel: getPaginationRowModel(),
}),
...(mergedConfig.sorting?.enabled && {
getSortedRowModel: getSortedRowModel(),
enableMultiSort: mergedConfig.sorting.multiSort,
}),
...(mergedConfig.filtering?.enabled && {
getFilteredRowModel: getFilteredRowModel(),
}),
...(mergedConfig.expandable?.enabled && {
getExpandedRowModel: getExpandedRowModel(),
}),
...(mergedConfig.rowSelection?.enabled && {
enableRowSelection: true,
}),
initialState: {
...(mergedConfig.pagination?.enabled && {
pagination: { pageSize: mergedConfig.pagination.pageSize || 10 },
}),
},
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onExpandedChange: setExpanded,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
expanded,
},
}), [
validation.isValid,
props.data,
props.columns,
mergedConfig,
sorting,
columnFilters,
columnVisibility,
rowSelection,
expanded
])
const table = useReactTable(tableConfig)
// Performance: Reset pagination only when data actually changes
const previousDataRef = useRef(props.data)
const setPageIndexRef = useRef(table.setPageIndex)
useEffect(() => {
setPageIndexRef.current = table.setPageIndex
}, [table.setPageIndex])
useEffect(() => {
if (mergedConfig.pagination?.enabled && previousDataRef.current !== props.data) {
setPageIndexRef.current(0)
previousDataRef.current = props.data
}
}, [props.data, mergedConfig.pagination?.enabled])
// Exposed methods for external control
const resetFilters = () => setColumnFilters([])
const resetSelection = () => setRowSelection({})
const resetSorting = () => setSorting([])
const resetAll = () => {
resetFilters()
resetSelection()
resetSorting()
setExpanded({})
}
const resetValidation = useCallback(() => setValidationErrors([]), [])
return {
table,
config: mergedConfig,
// State for direct access
sorting,
columnFilters,
columnVisibility,
rowSelection,
expanded,
// Validation
validation,
validationErrors,
// Methods
resetFilters,
resetSelection,
resetSorting,
resetAll,
resetValidation,
}
}

The template uses compound components for flexible, reusable UI:

Separation of Concerns

Each component has a single responsibility with clear interfaces

Composition over Inheritance

Components are composed together rather than extended

Prop Drilling Avoidance

Context and custom hooks prevent deep prop passing

Robust error handling with TypeScript:

src/components/ErrorBoundary.tsx
import React from 'react'
interface Props {
children: React.ReactNode
fallback?: React.ComponentType<{ error: Error }>
}
interface State {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error boundary caught an error:', error, errorInfo)
}
render() {
if (this.state.hasError && this.state.error) {
const Fallback = this.props.fallback
return Fallback ? <Fallback error={this.state.error} /> : (
<div className="p-4 border border-destructive/50 rounded-lg bg-destructive/5">
<h2 className="font-semibold text-destructive">Something went wrong</h2>
<p className="text-sm text-muted-foreground mt-1">
{this.state.error.message}
</p>
</div>
)
}
return this.props.children
}
}

Advanced form validation with React Hook Form and Zod:

src/components/HiForm/hooks/useFormValidation.ts
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import type { z } from 'zod'
export function useFormValidation({
fields,
schema,
defaultValues = {},
onChange
}: UseFormValidationProps) {
// Generate default values from fields if not provided
const generateDefaultValues = (): FormValues => {
const values: FormValues = { ...defaultValues }
fields.forEach(field => {
if (values[field.name] === undefined) {
switch (field.type) {
case 'checkbox':
if ('options' in field && field.options) {
values[field.name] = []
} else {
values[field.name] = false
}
break
case 'switch':
values[field.name] = false
break
case 'slider':
if ('config' in field && field.config.defaultValue) {
values[field.name] = field.config.defaultValue
} else if ('config' in field) {
values[field.name] = [field.config.min]
}
break
case 'select':
if ('multiple' in field && field.multiple) {
values[field.name] = []
} else {
values[field.name] = ''
}
break
case 'datePicker':
if ('config' in field && field.config?.mode === 'multiple') {
values[field.name] = []
} else if ('config' in field && field.config?.mode === 'range') {
values[field.name] = { from: undefined, to: undefined }
} else {
values[field.name] = undefined
}
break
default:
values[field.name] = ''
}
}
})
return values
}
const form = useForm({
resolver: schema ? zodResolver(schema as any) : undefined,
defaultValues: generateDefaultValues(),
mode: 'onChange'
})
const { watch, setValue, reset, formState } = form
const watchedValues = watch()
// Handle field changes - no useCallback needed for this implementation
const handleFieldChange = (name: string, value: FormValue) => {
setValue(name, value, { shouldValidate: true, shouldDirty: true })
if (onChange) {
const allValues = { ...watchedValues, [name]: value }
onChange(name, value, allValues)
}
}
// Get visible fields based on conditions
const getVisibleFields = () => {
return fields.filter(field => {
if (!field.condition) return true
return field.condition(watchedValues)
})
}
// Reset form with new values
const resetForm = (values?: FormValues) => {
reset(values || generateDefaultValues())
}
return {
form,
watchedValues,
handleFieldChange,
getVisibleFields,
resetForm,
isSubmitting: formState.isSubmitting,
isDirty: formState.isDirty,
isValid: formState.isValid,
errors: formState.errors
}
}

The template uses Zustand for lightweight, type-safe state management:

src/store/uiStore.ts
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
// UI State Management
interface UIState {
sidebarCollapsed: boolean
fixedSidebarVisible: boolean
layoutMode: LayoutMode
expandedMenuItems: string[]
// Actions
setSidebarCollapsed: (collapsed: boolean) => void
toggleSidebar: () => void
toggleMenuExpand: (menuTitle: string) => void
}
export const useUIStore = create<UIState>()(
persist(
(set, get) => ({
// Initial state
sidebarCollapsed: false,
fixedSidebarVisible: false,
layoutMode: 'sidebar' as LayoutMode,
expandedMenuItems: [],
// Synchronous actions
setSidebarCollapsed: (collapsed) =>
set({ sidebarCollapsed: collapsed }),
// Complex state updates
toggleMenuExpand: (menuTitle) =>
set((state) => ({
expandedMenuItems: state.expandedMenuItems.includes(menuTitle)
? state.expandedMenuItems.filter((item) => item !== menuTitle)
: [...state.expandedMenuItems, menuTitle]
})),
// Computed actions using get()
toggleSidebar: () =>
set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })),
}),
{
name: 'ui-storage',
storage: createJSONStorage(() => localStorage),
// Selective persistence
partialize: (state) => ({
layoutMode: state.layoutMode,
sidebarCollapsed: state.sidebarCollapsed
}),
}
)
)

Advanced theme management with system preference detection:

src/store/themeStore.ts
export const useThemeStore = create<ThemeState>()(
persist(
(set, get) => ({
theme: "system",
resolvedTheme: "light",
setTheme: (theme: Theme) => {
const resolvedTheme = applyTheme(theme)
set({ theme, resolvedTheme })
},
initialize: () => {
const { theme } = get()
const resolvedTheme = applyTheme(theme)
set({ resolvedTheme })
// System theme change listener
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleChange = () => {
const currentTheme = get().theme
if (currentTheme === 'system') {
const newResolvedTheme = getSystemTheme()
const root = window.document.documentElement
root.classList.remove("light", "dark")
root.classList.add(newResolvedTheme)
set({ resolvedTheme: newResolvedTheme })
}
}
mediaQuery.addEventListener('change', handleChange)
}
},
}),
{
name: 'admin-theme',
partialize: (state) => ({ theme: state.theme }),
}
)
)

Organize exports for better maintainability:

src/store/index.ts
// Store exports
export { useUIStore } from './uiStore'
export { useThemeColorStore } from './themeColorStore'
export { useThemeStore, useTheme } from './themeStore'
// Hook exports
export { useIsMobile, useIsDevice } from './useMobile'
export { useLocalStorage } from './useLocalStorage'
export { useMediaQuery, useMediaQueryCore } from './useMediaQuery'
// Component exports with type re-exports
export { HiTable } from './HiTable'
export type { HiTableProps, TableConfig } from './types'

Clean, type-safe utility functions:

src/lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
// Tailwind class utility with proper typing
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Number formatting utilities
export function formatNumber(value: number): string {
return new Intl.NumberFormat('en-US').format(value)
}
export function formatCurrency(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
}).format(value)
}
// Color validation utilities
export function isValidHexColor(color: string): boolean {
if (!color) return false
const hex = color.startsWith('#') ? color.slice(1) : color
const hexPattern = /^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/
return hexPattern.test(hex)
}
// Name utility with proper error handling
export function getInitials(name: string, maxInitials: number = 2): string {
if (!name || typeof name !== 'string') return ''
return name
.trim()
.split(' ')
.filter(word => word.length > 0)
.slice(0, maxInitials)
.map(word => word[0])
.join('')
.toUpperCase()
}
  1. Memoization Strategy

    • Use useMemo for expensive calculations
    • Implement useCallback for stable function references
    • Optimize dependency arrays to prevent unnecessary re-renders
  2. Bundle Optimization

    • Implement code splitting for route-based chunks
    • Use dynamic imports for large dependencies
    • Tree-shake unused code with proper ES6 imports
  3. State Management

    • Minimize state updates and batch when possible
    • Use Zustand’s selective subscriptions
    • Implement proper state normalization
  4. Component Optimization

    • Apply React.memo for pure components
    • Use compound patterns to avoid prop drilling
    • Implement error boundaries for graceful failures

Comprehensive linting setup for code quality:

eslint.config.js
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { globalIgnores } from 'eslint/config'
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

Unit Tests

Test individual functions and component logic in isolation

Integration Tests

Test component interactions and hook behaviors

Accessibility Tests

Ensure WCAG compliance and keyboard navigation

Performance Tests

Monitor rendering performance and memory usage

Enable development debugging utilities:

src/utils/debug.ts
// Development-only debugging utilities
if (process.env.NODE_ENV === 'development') {
// Global debug helpers
window.debugState = {
ui: () => useUIStore.getState(),
theme: () => useThemeStore.getState(),
themeColor: () => useThemeColorStore.getState(),
}
// Performance monitoring
window.measureRender = (componentName: string) => {
performance.mark(`${componentName}-start`)
return () => {
performance.mark(`${componentName}-end`)
performance.measure(componentName, `${componentName}-start`, `${componentName}-end`)
}
}
}
  1. TypeScript Configuration

    • Enable strict mode and all strict flags
    • Define comprehensive interfaces for all data
    • Use path mapping for clean imports
  2. Component Design

    • Implement single responsibility principle
    • Use composition over inheritance
    • Add proper error boundaries
  3. Performance

    • Memoize expensive calculations
    • Optimize re-renders with proper dependencies
    • Implement code splitting for large features
  4. State Management

    • Use appropriate state management (local vs global)
    • Implement proper state persistence
    • Avoid unnecessary state subscriptions
  5. Code Quality

    • Configure ESLint with strict rules
    • Implement comprehensive error handling
    • Add proper TypeScript typing

Master these React development patterns to build scalable, maintainable, and high-performance applications! 🚀