2024-08-24 by Remi Kristelijn
After building our Next.js blog template and seeing it grow from a simple project to a community-driven platform, it's time to take a step back and analyze the code quality. This post documents a comprehensive quality audit, identifies areas for improvement, and implements best practices learned from production systems.
Our current codebase consists of:
// Current approach - many custom overrides sx={{ '& h1': { fontSize: '2rem', fontWeight: 700, mb: 2, mt: 3 }, '& h2': { fontSize: '1.75rem', fontWeight: 600, mb: 1.5, mt: 2.5 }, '& h3': { fontSize: '1.5rem', fontWeight: 600, mb: 1, mt: 2 }, // ... many more overrides }} // Better approach - use theme tokens sx={{ '& h1': (theme) => theme.typography.h1, '& h2': (theme) => theme.typography.h2, '& h3': (theme) => theme.typography.h3, }}
// 125 lines of mixed concerns export default function Mermaid({ chart, id }: MermaidProps) { // Theme configuration // Rendering logic // Error handling // Styling // All in one component }
/** * Hook for managing Mermaid configuration and rendering * @param chart - Mermaid diagram syntax * @param id - Optional unique identifier * @returns Rendering state and error handling */ export function useMermaidRenderer(chart: string, id?: string) { // Configuration and rendering logic } /** * Mermaid diagram component with theme integration * @param props - Chart configuration and styling options */ export default function Mermaid({ chart, id, className }: MermaidProps) { const { elementRef, error, isLoading } = useMermaidRenderer(chart, id); if (error) return <MermaidError error={error} />; if (isLoading) return <MermaidSkeleton />; return <MermaidContainer ref={elementRef} className={className} />; }
/** * Extended Material-UI theme with custom tokens */ export const blogTheme = createTheme({ // Standard MUI theme palette: { primary: { main: '#1976d2' }, secondary: { main: '#dc004e' }, }, // Custom tokens for blog-specific styling components: { MuiTypography: { styleOverrides: { h1: ({ theme }) => ({ fontSize: '2rem', fontWeight: 700, marginBottom: theme.spacing(2), marginTop: theme.spacing(3), }), }, }, }, // Custom spacing for consistent layouts custom: { layout: { contentMaxWidth: '800px', sidebarWidth: '300px', }, blog: { postSpacing: 3, cardElevation: 2, }, }, });
/** * Blog post content with standardized typography */ export default function PostContent({ post }: PostContentProps) { return ( <Box sx={{ mb: 4 }}> <Typography variant="h1" component="h1" gutterBottom> {post.title} </Typography> <BlogMeta date={post.date} author={post.author} /> <BlogContent> <ReactMarkdown components={markdownComponents}> {post.content} </ReactMarkdown> </BlogContent> </Box> ); }
const features: Feature[] = [ { icon: 'π', title: 'Fast', description: 'Built with Next.js for optimal performance' }, // Hardcoded in component ];
/** * Blog configuration and content data */ export const blogConfig = { features: [ { id: 'performance', icon: 'π', title: 'Fast Performance', description: 'Built with Next.js 15 and optimized for speed', metrics: { lighthouse: 95, coreWebVitals: 'good' }, }, ], navigation: { primary: [ { label: 'Home', href: '/', icon: 'home' }, { label: 'Posts', href: '/posts', icon: 'article' }, ], }, } as const; /** * Type-safe configuration access */ export type BlogConfig = typeof blogConfig; export type Feature = BlogConfig['features'][0];
/** * Enhanced error boundary with logging and recovery */ export class BlogErrorBoundary extends Component< ErrorBoundaryProps, ErrorBoundaryState > { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error: Error): ErrorBoundaryState { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { // Log to monitoring service this.logError(error, errorInfo); // Report to analytics this.reportError(error, errorInfo); } private logError = (error: Error, errorInfo: ErrorInfo) => { if (process.env.NODE_ENV === 'development') { console.error('Blog Error:', error, errorInfo); } // In production, send to monitoring service // monitoringService.captureException(error, { extra: errorInfo }); }; render() { if (this.state.hasError) { return ( <ErrorFallback error={this.state.error} onRetry={() => this.setState({ hasError: false, error: null })} /> ); } return this.props.children; } }
/** * Hook for managing blog post data with caching */ export function useBlogPosts() { const [posts, setPosts] = useState<Post[]>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { const loadPosts = async () => { try { setLoading(true); const postsData = await getAllPosts(); setPosts(postsData); } catch (err) { setError(err instanceof Error ? err : new Error('Failed to load posts')); } finally { setLoading(false); } }; loadPosts(); }, []); return { posts, loading, error, refetch: () => loadPosts() }; } /** * Hook for theme-aware styling */ export function useThemeStyles() { const theme = useTheme(); return useMemo(() => ({ contentContainer: { maxWidth: theme.custom.layout.contentMaxWidth, margin: '0 auto', padding: theme.spacing(0, 2), }, blogCard: { elevation: theme.custom.blog.cardElevation, spacing: theme.custom.blog.postSpacing, }, }), [theme]); }
// src/test-utils/setup.ts import '@testing-library/jest-dom'; import { render, RenderOptions } from '@testing-library/react'; import { ThemeProvider } from '@mui/material/styles'; import { blogTheme } from '../lib/theme'; /** * Custom render function with theme provider */ export function renderWithTheme( ui: React.ReactElement, options?: RenderOptions ) { const Wrapper = ({ children }: { children: React.ReactNode }) => ( <ThemeProvider theme={blogTheme}> {children} </ThemeProvider> ); return render(ui, { wrapper: Wrapper, ...options }); } export * from '@testing-library/react'; export { renderWithTheme as render };
// src/components/__tests__/PostCard.test.tsx import { render, screen } from '../../test-utils/setup'; import PostCard from '../PostCard'; import { mockPost } from '../../test-utils/mocks'; describe('PostCard', () => { it('renders post information correctly', () => { render(<PostCard post={mockPost} />); expect(screen.getByText(mockPost.title)).toBeInTheDocument(); expect(screen.getByText(mockPost.excerpt)).toBeInTheDocument(); expect(screen.getByText(mockPost.date)).toBeInTheDocument(); }); it('navigates to post when clicked', () => { const { container } = render(<PostCard post={mockPost} />); const link = container.querySelector('a'); expect(link).toHaveAttribute('href', `/posts/${mockPost.slug}`); }); it('handles missing optional fields gracefully', () => { const postWithoutAuthor = { ...mockPost, author: undefined }; expect(() => { render(<PostCard post={postWithoutAuthor} />); }).not.toThrow(); }); });
// src/hooks/__tests__/useBlogPosts.test.ts import { renderHook, waitFor } from '@testing-library/react'; import { useBlogPosts } from '../useBlogPosts'; import * as postsLib from '../../lib/posts'; jest.mock('../../lib/posts'); const mockGetAllPosts = postsLib.getAllPosts as jest.MockedFunction<typeof postsLib.getAllPosts>; describe('useBlogPosts', () => { beforeEach(() => { jest.clearAllMocks(); }); it('loads posts successfully', async () => { const mockPosts = [{ id: '1', title: 'Test Post' }]; mockGetAllPosts.mockResolvedValue(mockPosts); const { result } = renderHook(() => useBlogPosts()); expect(result.current.loading).toBe(true); await waitFor(() => { expect(result.current.loading).toBe(false); }); expect(result.current.posts).toEqual(mockPosts); expect(result.current.error).toBeNull(); }); it('handles errors gracefully', async () => { const error = new Error('Failed to load'); mockGetAllPosts.mockRejectedValue(error); const { result } = renderHook(() => useBlogPosts()); await waitFor(() => { expect(result.current.loading).toBe(false); }); expect(result.current.error).toEqual(error); expect(result.current.posts).toEqual([]); }); });
/** * Memoized post card for list performance */ export const PostCard = memo(function PostCard({ post }: PostCardProps) { const styles = useThemeStyles(); return ( <Card sx={styles.blogCard}> <CardContent> <Typography variant="h5" component="h2"> {post.title} </Typography> <Typography variant="body2" color="text.secondary"> {post.excerpt} </Typography> </CardContent> </Card> ); });
/** * Lazy-loaded Mermaid component for better initial load */ const Mermaid = lazy(() => import('./Mermaid')); export function LazyMermaid(props: MermaidProps) { return ( <Suspense fallback={<MermaidSkeleton />}> <Mermaid {...props} /> </Suspense> ); }
// webpack-bundle-analyzer integration const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer({ // Next.js config });
src/ βββ components/ β βββ ui/ # Reusable UI components β βββ blog/ # Blog-specific components β βββ __tests__/ # Component tests βββ hooks/ # Custom React hooks βββ lib/ # Utility functions βββ types/ # TypeScript definitions βββ config/ # Configuration files βββ test-utils/ # Testing utilities
/** * Strict type definitions with proper documentation */ export interface Post { /** Unique identifier for the post */ readonly id: string; /** SEO-friendly URL slug */ readonly slug: string; /** Post title for display and SEO */ title: string; /** Brief description for previews and SEO */ excerpt: string; /** Full post content in markdown format */ content: string; /** Publication date in ISO format */ date: string; /** Optional author information */ author?: string; /** Optional featured image URL */ featuredImage?: string; } /** * Type-safe post operations */ export type PostOperations = { readonly getAllPosts: () => Promise<readonly Post[]>; readonly getPostBySlug: (slug: string) => Promise<Post | null>; readonly searchPosts: (query: string) => Promise<readonly Post[]>; };
/** * Result type for operations that can fail */ export type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; /** * Safe post loading with proper error handling */ export async function safeGetAllPosts(): Promise<Result<Post[]>> { try { const posts = await getAllPosts(); return { success: true, data: posts }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Unknown error') }; } }
/** * Performance monitoring for blog operations */ export function withPerformanceMonitoring<T extends (...args: any[]) => any>( fn: T, operationName: string ): T { return ((...args: Parameters<T>) => { const start = performance.now(); try { const result = fn(...args); if (result instanceof Promise) { return result.finally(() => { const duration = performance.now() - start; logPerformance(operationName, duration); }); } const duration = performance.now() - start; logPerformance(operationName, duration); return result; } catch (error) { const duration = performance.now() - start; logError(operationName, error, duration); throw error; } }) as T; }
/** * Privacy-focused analytics for blog engagement */ export const analytics = { trackPageView: (path: string) => { // Track page views without personal data if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') { // Send to analytics service } }, trackPostRead: (postId: string, readTime: number) => { // Track reading engagement }, trackError: (error: Error, context: string) => { // Track errors for improvement }, };
This code quality analysis transformed our blog template from a functional prototype into a production-ready platform. By implementing proper separation of concerns, comprehensive testing, performance monitoring, and following TypeScript best practices, we've created a maintainable and scalable codebase.
The improvements resulted in:
Quality code isn't just about following rulesβit's about creating a foundation that enables rapid development, easy maintenance, and confident deployments. These practices ensure our blog template can continue to evolve and serve the community effectively.
Quality is not an act, it is a habit. - Aristotle