Monorepo stores multiple related projects in single repository with shared version control. Structure: apps/ (deployable applications), packages/ (shared libraries), all using same node_modules and git history. Single source of truth for entire codebase. Examples: Google uses single monorepo for entire company codebase, Facebook (Meta) similar approach. Key characteristic: All code commits happen in one repository, enabling atomic changes across projects. Contrast with polyrepo: separate repository per project/service. Essential for: Code sharing across projects, coordinated releases, unified tooling. Requires tooling: Nx, Turborepo, or pnpm workspaces to manage builds efficiently at scale. Without proper tools, monorepos become slow and unwieldy.
Code Organization 2025 FAQ & Answers
32 expert Code Organization 2025 answers researched from official documentation. Every answer cites authoritative sources you can verify.
unknown
32 questionsChoose monorepo when: (1) Team <50 developers with high coordination needs, (2) 3-20 related projects sharing code (web + mobile + API), (3) High dependency coupling between services, (4) Need coordinated releases and unified tooling. Choose polyrepo when: (1) Team >100 developers with autonomous teams, (2) 50+ independent services/microservices, (3) Loose coupling between projects, (4) Independent deployment schedules per service. 2025 thresholds: Monorepo requires tooling (Nx/Turborepo) beyond 10 projects or build time degrades. Git performance suffers with millions of commits. IDE breaks with 100K+ files without proper indexing. Hybrid approach: Group related services in monorepo, keep unrelated projects separate. Don't choose based on hype - assess team topology, dependency coupling, and deployment cadence. Wrong choice costs 6+ months to migrate.
Nx is comprehensive monorepo tool with advanced features and extensive ecosystem. Capabilities: (1) Code generation - create components/apps with consistent structure, (2) Visual dependency graph - visualize project relationships, (3) Computation caching - skip unchanged tasks (local + remote), (4) Affected commands - run tasks only for changed projects, (5) Plugin ecosystem - Angular, React, Node.js, Nest, (6) Multi-language support - JavaScript, TypeScript, Java, Go. Benefits: Opinionated structure, enforces best practices, prevents circular dependencies. Drawbacks: Complex setup, steep learning curve, migration cost. Use when: Large/complex projects, need long-term maintainability, starting new project and want structure, polyglot teams. 2025 status: Battle-tested, strong community, well-established. Don't use: Simple projects, existing monorepo with light needs. Alternative: Turborepo for simplicity.
Turborepo is fast, minimal monorepo build system focused on execution speed. Key features: (1) Incremental builds - cache task results, 30s build → 0.2s from cache, (2) Pipeline orchestration - define task dependencies in turbo.json, (3) Remote caching - share cache across CI and team (Vercel integration), (4) Flexible - doesn't force restructuring, works with existing monorepo. Benefits: Add to existing monorepo in <10 minutes, minimal migration cost, stays out of your way, integrates with pnpm/yarn/npm. Drawbacks: Only solves build speed, no architecture enforcement, no code generation, team discipline required. Use when: Main problem is slow builds/CI, existing monorepo, senior disciplined team, value flexibility over structure. 2025 status: Vercel-backed, growing adoption. Combine with pnpm for best results. Don't use: Need architecture guidance, starting new project wanting opinionated structure.
pnpm workspaces provide monorepo capabilities with content-addressable storage (CAS) - stores packages once in global store indexed by content hash, creates hard links to projects. 80% disk space savings vs npm. Features: (1) Fast installs - reuse cached packages across all projects, (2) Strict dependency resolution - prevents phantom dependencies, (3) Workspace protocol - link local packages with workspace:, (4) Single lockfile - sharedWorkspaceLockfile: true ensures consistent versions, (5) Filter commands - pnpm --filter run tests only in affected packages. Configuration: Define in pnpm-workspace.yaml: packages: ['apps/', 'packages/*']. 2025 pattern: pnpm (package manager) + Turborepo (build orchestration) for minimal high-performance setup. Hard links/reflinks share package files across projects. Use when: Need efficient dependency management, monorepo without heavy tooling. Essential for workspace dependency management in 2025.
Choose Turborepo when: (1) Slow builds are main problem, (2) Existing monorepo (don't want costly migration), (3) Senior disciplined team can self-manage architecture, (4) Value minimalism and flexibility, (5) JavaScript/TypeScript focused. Choose Nx when: (1) Starting new project and want structure from day one, (2) Large/complex project needs long-term maintainability, (3) Need code generation and scaffolding, (4) Want dependency graph visualization, (5) Polyglot teams (multi-language support), (6) Need architectural enforcement. 2025 comparison: Turborepo focuses on speed (incremental builds, remote caching), Nx provides ecosystem (plugins, generators, graph, enforcement). Migration: Turborepo add in <10 min, Nx requires restructuring. Both offer remote caching. Hybrid: Start with Turborepo for quick wins, migrate to Nx if complexity grows. Don't choose based on hype - assess team maturity and project needs.
Yes, pnpm + Turborepo is recommended 2025 combination for high-performance monorepos. pnpm handles dependencies (80% disk savings, fast installs), Turborepo handles builds (caching, pipeline orchestration). Measured performance: 30s build → 0.2s with Turborepo cache. CI pipeline: 2+ minutes → 10 seconds with pnpm caching (83% faster). Nhost achieved 7-minute total CI time (25% improvement). Setup: (1) Create pnpm-workspace.yaml with packages: ['apps/', 'packages/'], (2) Add Turborepo with turbo.json defining pipeline, (3) Configure tasks with dependencies: build: { dependsOn: ['^build'] }. Benefits: pnpm's content-addressable storage + Turborepo's incremental builds = blazing-fast monorepo. 2025 pattern: pnpm for packages, Turborepo for tasks, single package.json scripts. Seamless integration - Turborepo auto-detects pnpm. Alternative: pnpm + Nx for more features. Essential combination for performance-critical monorepos.
Feature-based structure organizes code by domain/feature, not technical layer. All related code for feature grouped together. Example: features/auth/ contains login.component.tsx, auth.service.ts, auth.test.ts. Benefits: (1) Cohesion - related code stays together, (2) Scalability - add features without touching other folders, (3) Team ownership - teams own complete features, (4) Easy extraction - feature can become microservice. Structure: features/auth/, features/users/, features/orders/. Each feature folder may contain: components/, services/, hooks/, types/, tests/. 2025 recommendation: Feature-based for apps >10 features, follows DDD principles. Use when: Clear business domains, multiple teams, expect growth. Don't use: Tiny apps (<5 components), simple CRUD. Alternative: Layered structure (components/, services/, utils/) for small apps. Modern pattern: Hybrid - feature-based with layers inside each feature.
Layered architecture separates application into horizontal technical layers: Presentation (views/controllers), Business (domain logic), Data (repositories), Persistence (database). Structure: components/, services/, utils/, types/. Example: components/LoginForm.tsx, services/authService.ts. Benefits: (1) Technical clarity - all components centralized, (2) Familiarity - traditional approach, (3) Simple for small apps (<20 components). Drawbacks: (1) Poor cohesion - auth logic scattered across 4 folders, (2) Doesn't scale - 100+ components unwieldy, (3) Hard to extract - code spread across layers, (4) Tight coupling - changes ripple across layers. 2025 verdict: Outdated for medium+ apps. Use only for: Simple CRUD, prototypes, single developer, <20 components, cheap/fast projects. Modern alternative: Vertical Slice (feature-based) or Hybrid (Clean Architecture + Vertical Slices) recommended for 2025. Don't use for: Multi-team projects, complex domains, microservices extraction.
Hybrid structure: Package by feature first, then by layer inside each feature. Combines benefits of both approaches. Structure: features/auth/components/, features/auth/services/, features/users/components/, features/users/hooks/. Each feature is vertical slice with all technical layers inside. Benefits: (1) Feature cohesion - all auth code in features/auth/, (2) Layer clarity - components separate from services within feature, (3) Scalable - add features without impacting others, (4) Extractable - feature folder can become microservice with all layers intact. DDD alignment: Each bounded context maps to feature folder. 2025 recommendation: Default for medium+ applications. Use when: App has distinct features/domains, team organized by feature, need both cohesion and layer separation. Implementation: If need to break functionality into service, it's packaged ready to move. Don't over-layer - keep 2-3 layers max per feature (components, services, types).
DDD projects organize by bounded context (business domain), then tactical patterns within. Structure: src/contexts/orders/ (bounded context), contexts/orders/domain/ (entities, value objects, aggregates), contexts/orders/application/ (use cases, commands/queries), contexts/orders/infrastructure/ (repositories, adapters), contexts/orders/presentation/ (controllers, DTOs). Bounded context is logical boundary where domain terms and rules apply consistently. Benefits: Clear domain boundaries, independent deployment, testable domain logic, microservices-ready. 2025 pattern: contexts/ (or modules/) for bounded contexts, domain/application/infrastructure layers inside. Example: E-commerce contexts: catalog, ordering, shipping, payment. Each context has own ubiquitous language. Process: (1) Strategic DDD - identify bounded contexts, (2) Tactical DDD - define aggregates/entities within contexts. Don't: Create god context, split single domain. Module per bounded context - enables encapsulation and extraction. Essential for complex business logic in 2025.
Organize by feature for apps >20 components, by layer only for tiny apps. Feature-based advantages: (1) Cohesion - related code grouped, (2) Team ownership - teams own features end-to-end, (3) Scalability - add features independently, (4) Microservices-ready - extract features easily. Layer-based advantages: (1) Simplicity - all components in one place, (2) Familiarity - traditional approach. 2025 consensus: Feature-based is modern best practice for scalable applications. Hybrid approach recommended: Package by feature first, layer inside feature. When to use each: Layer-based for prototypes, simple tools, <20 components. Feature-based for production apps, >20 components, multiple teams, domain complexity. Migration path: Start layered (fast), refactor to feature-based when complexity grows (>20 components or multiple developers). Don't: Mix approaches inconsistently, use layered for large apps. Essential: Align with team structure (Conway's Law).
Barrel export is file (typically index.ts) that re-exports from other files. Consolidates imports in single location. Pattern: components/index.ts exports all components, allows: import { Button, Card } from './components' instead of import Button from './components/Button'. Intended benefit: Cleaner imports, single entry point. Reality: Causes severe problems in 2025. Issues: (1) Breaks tree-shaking - bundler thinks everything is needed, 12MB bundle vs 1MB without barrels, (2) Dev performance - 11k modules loaded vs 3.5k (68% reduction removing barrels), yarn next dev takes minute longer, (3) Module resolution overhead. 2025 verdict: Avoid barrel exports in application code. Only acceptable: Library entry points (single public API), TypeScript type definitions (no runtime impact). Use direct imports: import Button from './components/Button'. Don't create index.ts files arbitrarily.
Barrel exports harm bundle size and development performance. Measured impacts: (1) Bundle size - 12MB with barrels vs <1MB without (12x larger), index files trick bundler into including unused code, (2) Development performance - Pages loaded 11k modules (5-10s startup) vs 3.5k modules without barrels (68% reduction), yarn next dev takes minute longer with barrels, (3) Tree-shaking broken - Webpack/Vite can't eliminate dead code from barrel re-exports. Why: Barrel creates dependency on entire module, bundler includes all exports even if only one used. CommonJS barrels don't support tree-shaking at all. 2025 tools (Vite, Next.js) still struggle with barrel optimization. Real projects: Removing barrels dramatically improved build times and bundle sizes. Only acceptable for: Library public API (necessary), TypeScript types (no runtime). Don't use in app code - massive performance penalty.
Barrel exports break tree-shaking by obscuring direct import-module relationships. With barrels: import { Button } from './components' loads index.ts re-exporting Button, Card, Input, Form. Bundler parses entire barrel file to find correct export, includes unnecessary code. Measured impact: 1200% larger bundles (12MB vs <1MB), 48% bundle reduction removing barrels. CommonJS barrels: zero tree-shaking support. ESM barrels: Better but bundlers still struggle. Next.js 13.5+ solution: optimizePackageImports automatically maps barrel imports, achieving 28% faster builds, 10% faster dev server, 40% faster serverless cold starts. Configuration: Set "sideEffects": false in package.json if all modules pure. Problem: Re-export creates runtime dependency on all modules - bundler can't determine which exports needed until runtime. 2025 reality: Modern bundlers improving (Next.js auto-optimization) but direct imports remain best practice. Solution: import Button from './components/Button' for optimal tree-shaking.
Barrel exports acceptable only in specific scenarios: (1) Library entry points - npm packages need single public API, set package.json main field to index.js re-exporting public API. Libraries like @tanstack/react-query require barrel for consumers: import { useQuery } from 'library'. Essential for library design. (2) TypeScript type definitions - types have zero runtime impact, safe to barrel: import type { User, Post } from './types'. Use export type and import type exclusively. (3) Logically correlated code - files implementing single feature/purpose can barrel (backend communication modules). Avoid deeply nested barrels - prefer export { specific } from './file' over export * from './anything'. Never use for: (1) Application code - components, services in app (5-10s dev startup penalty, 68% more modules loaded), (2) Performance-critical code - bundle size affected. 2025 guideline: Library = barrel required. App = avoid barrels. Use direct imports for optimal performance.
Use direct imports from specific files. Pattern: import Button from './components/Button'; import Card from './components/Card'. Benefits: (1) Tree-shaking works perfectly - bundler includes only imported modules, (2) Fast dev server - load only needed modules (3.5k vs 11k modules), (3) Explicit dependencies - clear which files import which, (4) Better IDE performance - faster autocomplete, go-to-definition. Editor setup: Modern IDEs auto-import from correct files, TypeScript auto-import handles paths, VS Code/WebStorm support absolute imports (import Button from '@/components/Button'). Path aliases: Configure tsconfig.json paths for cleaner imports without barrels. Example: '@/components/': ['src/components/']. 2025 recommendation: Embrace explicit imports, configure path aliases for convenience, let IDE handle import management. Don't: Create index.ts files to "clean up" imports - false economy that breaks tree-shaking. Performance over convenience.
Configure workspaces in root package.json with workspaces field. Setup: (1) Create root package.json with "private": true (prevents accidental publish), "workspaces": ["apps/", "packages/"], (2) Create workspace folders: apps/ for applications, packages/ for shared libraries, (3) Each workspace has own package.json with name, version, dependencies. Structure: Root node_modules (hoisted dependencies), workspace-specific node_modules (conflicting versions). Install dependencies: npm install (root, installs all), npm install -w app-name (specific workspace). Run scripts: npm run build -w app-name, npm run test --workspaces (all workspaces). Benefits: Shared dependencies hoisted, single lock file, atomic commits. 2025 pattern: Use glob patterns (packages/), organize by type (apps/, packages/), link workspaces with workspace: protocol. Essential for monorepo with npm.
workspace:* is protocol for linking local workspace packages as dependencies. Syntax in package.json: "dependencies": {"@monorepo/shared": "workspace:"}. Behavior: Links to local workspace package, not npm registry. Star () means "use current version". Benefits: (1) Always uses local version - changes immediately available, (2) Type-safe - TypeScript resolves to local types, (3) No publish needed for development, (4) npm/pnpm handle linking automatically. Alternatives: workspace:^ (semver range), workspace:~ (tilde range). Use * for simplicity. Package managers: npm 7+, pnpm, yarn support workspace protocol. When published: Resolves to actual version number. Example: app-web depends on shared utils: {"@monorepo/utils": "workspace:"}. Changes in utils immediately available in app-web without reinstall. 2025 standard: Essential for monorepo cross-package dependencies. Use workspace: for all internal dependencies.
Dependency hoisting moves shared dependencies to root node_modules, avoiding duplicates. How it works: If multiple workspaces use same package and version, hoisted to root. If different versions needed, installed in workspace-specific node_modules. Benefits: (1) Disk space savings - one copy instead of N copies, (2) Faster installs - download once, (3) Consistent versions across workspaces. Example: app-web and app-mobile both use [email protected] → installed at root. app-admin uses [email protected] → installed in app-admin/node_modules/. Resolution: Node resolves dependencies upward - checks workspace node_modules, then root. 2025 behavior: npm/pnpm/yarn all hoist by default. pnpm uses symlinks for extra efficiency (content-addressable storage). Potential issues: Phantom dependencies (using uninstalled package that's hoisted), fix with strict dependency checking. Essential understanding for monorepo dependency management.
apps/ for deployable applications, packages/ for shared libraries and tooling. apps/ (deployables): Web apps, mobile apps, backend services, CLI tools. Each has own deployment pipeline. Examples: apps/web/, apps/api/, apps/admin/. packages/ (shared code): UI components, utils, configs, types, business logic libraries. Reused across apps. Examples: packages/ui/, packages/utils/, packages/config/. Benefits: (1) Clear separation - apps consume packages, (2) Reusability - packages shared across apps, (3) Independent deployment - apps deploy separately, packages don't deploy, (4) Versioning - packages can version, apps don't need versions. 2025 recommendation: Split packages further: packages/ui/, packages/tooling/, packages/configs/. Don't: Mix concerns (app code in packages/), create circular dependencies (packages depend on apps). Typical monorepo: 3-10 apps, 10-50 packages. Essential structure for scaling monorepo.
pnpm catalogs (v9.5+) centralize dependency versions as reusable constants in pnpm-workspace.yaml. Workspaces reference with catalog: protocol. Configuration: Define in pnpm-workspace.yaml: catalogs: { default: { react: "18.2.0", typescript: "5.3.0" } }. Usage in package.json: "dependencies": { "react": "catalog:" }. Resolves to catalog version. When published, resolves to actual version number. Benefits: (1) Single source of truth - update once, applies everywhere, (2) No merge conflicts - package.json files unchanged during upgrades, (3) Named catalogs - multiple catalogs for different contexts (default, react17, legacy), (4) Strict enforcement - catalogMode: strict prevents direct version specs. 2025 tooling: Dependabot supports pnpm catalogs (Feb 2025), prevents broken dependency trees in CI. Use when: Many workspaces, frequent upgrades, version consistency critical. Essential for large monorepo dependency management in 2025.
Code splitting divides application bundle into smaller chunks loaded on-demand or in parallel. Instead of one large bundle (1MB), create multiple smaller bundles (100KB initial, 50KB per route). Benefits: (1) Faster initial load - load only needed code, (2) Better caching - unchanged chunks cached, (3) Parallel loading - load chunks concurrently, (4) Resource prioritization - critical code first. Methods: (1) Entry point splitting - multiple entry points in webpack config, (2) Dynamic imports - import() function splits automatically, (3) Vendor splitting - separate vendor code from app code. 2025 tools: Webpack SplitChunksPlugin (automatic), Vite built-in (zero-config), Next.js automatic route-based splitting. Use when: Large bundles (>500KB), route-based apps, vendor libraries large. Metrics improved: First Contentful Paint, Time to Interactive. Essential for production web apps. Don't: Over-split (100+ chunks causes overhead).
Lazy loading delays loading code until actually needed. Load component/module only when user navigates to it. Implementation: Use dynamic imports - import('./Component').then(). Benefits: (1) Reduced initial bundle - code not on first page excluded from initial load, (2) Faster startup - less JavaScript to parse/execute, (3) Better Core Web Vitals - improves FCP, LCP, TTI. Use cases: Route-based (load route components on navigation), Modal/dialog (load when opened), Below-fold content (load when scrolled into view), Feature flags (load features when enabled). React pattern: React.lazy(() => import('./Component')) + Suspense fallback. 2025 metrics: LCP improved 20-40% with lazy loading. Trade-off: Small delay when loading lazy component (show loading state). Use when: Large app with distinct sections, features not immediately visible, modal-heavy UIs. Essential for performance optimization.
Webpack offers three code splitting approaches: (1) Entry points - define multiple entries in webpack.config.js: entry: { app: './app.js', vendor: './vendor.js' }. Manual control, creates separate bundles. (2) SplitChunksPlugin (v4+ default) - automatic vendor/common splitting: optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /node_modules/, name: 'vendors' } } } }. Automatically splits node_modules into vendor chunk. (3) Dynamic imports - use import() for runtime splitting: const Component = () => import('./Component'). Webpack creates separate chunk automatically. Configuration: Set output.chunkFilename: '[name].[contenthash].js' for dynamic chunks. 2025 defaults: SplitChunksPlugin enabled, chunks >20KB split, maxParallelRequests limits enforced. Monitor: webpack-bundle-analyzer visualizes chunk sizes. Don't: Create tiny chunks (<10KB overhead), over-configure (defaults work well). Essential for large apps.
Vite has zero-config code splitting via Rollup (production builds). Automatic splitting: (1) Dynamic imports - use import('./Component'), Vite creates chunks automatically. (2) Vendor splitting - node_modules separated by default (Vite 2.9+: use splitVendorChunkPlugin). (3) ES modules - native ESM in dev, no bundling needed. Benefits: 60% faster dev servers, 25% quicker production builds vs Webpack. Implementation: Just use dynamic imports - const Component = () => import('./Component'). Vite handles rest. Advanced configuration: build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'] } } } }. 2025 performance: Native ES modules approach, active ecosystem, ideal for modern JavaScript apps. Zero-config philosophy - minimal setup required. Monitor: vite-bundle-visualizer for chunk analysis. Don't: Over-configure (defaults optimized), disable splitting (chunks critical for caching). Essential for performance-critical Vite projects.
React.lazy enables lazy loading React components with dynamic imports. Syntax: const Component = React.lazy(() => import('./Component')). Returns component that can be rendered inside Suspense boundary. Usage: <Suspense fallback={
Code splitting improves Core Web Vitals and user experience. Measured benefits: (1) First Contentful Paint (FCP) - 20-40% faster by reducing initial bundle size, user sees content sooner. (2) Largest Contentful Paint (LCP) - Faster main content render, loads critical resources first. (3) Time to Interactive (TTI) - 30-50% faster, less JavaScript to parse/execute. (4) Bundle size - 50-80% reduction in initial bundle (1MB → 200KB typical). (5) Caching - Unchanged chunks cached, only new code downloaded on updates. Real-world: Apps with code splitting: 2-3s initial load vs 5-10s without. User retention: 20% higher conversion with <3s load time. Use cases: Route-based splitting (load each route separately), vendor splitting (separate react/libraries from app code), feature splitting (admin panel loaded only for admins). 2025 target: <200KB initial bundle, <3s TTI. Essential for production performance.
Use consistent case and format for maintainability. Conventions: (1) Components - PascalCase or kebab-case: UserProfile.tsx or user-profile.tsx. React community prefers PascalCase matching component name. (2) Utilities/services - camelCase or kebab-case: authService.ts or auth-service.ts. Kebab-case recommended for cross-platform compatibility. (3) Tests - match source file with .test or .spec suffix: UserProfile.test.tsx. (4) Dates - YYYYMMDD format (20250112) for chronological sorting. (5) Avoid spaces - use hyphens or underscores, many systems can't handle spaces. (6) Alphanumeric only - avoid special characters (@, !, #). (7) Sequential numbering - use leading zeros (001, 010, 100) for proper sorting. 2025 recommendation: kebab-case for files (user-profile.tsx), PascalCase for components (UserProfile), camelCase for variables. Consistency more important than specific choice. Don't mix conventions within project.
Follow case conventions for readability and consistency. Standards: (1) camelCase for variables and functions - userName, getUserData(), industry standard in JavaScript. (2) PascalCase for classes and constructors - UserService, DatabaseConnection. (3) UPPER_SNAKE_CASE for constants - API_KEY, MAX_RETRY_COUNT. (4) Prefix booleans with is/has/should - isActive, hasPermission, shouldUpdate (improves readability). (5) Meaningful names - avoid single letters except loop counters (i, j), use descriptive names explaining purpose. (6) Use delimiters - camelCase or snake_case, not mixedCASEwith_underscores. 2025 TypeScript additions: Prefix interfaces with I (IUser) or use PascalCase without prefix (User - modern preference). Avoid: one-character variables in production code, abbreviations (usr instead of user), Hungarian notation (strName). Consistency within codebase essential. Use linter (ESLint) to enforce conventions.
React components use PascalCase consistently. Rules: (1) Component names - PascalCase: UserProfile, NavBar, AuthButton. Matches JSX usage:
Next.js has specific file naming requirements for routing. Special files (reserved names): page.tsx (route page), layout.tsx (shared layout), loading.tsx (loading UI), error.tsx (error boundary), not-found.tsx (404 page), route.ts (API route). Components: kebab-case recommended - user-profile.tsx for compatibility. Route folders: kebab-case - app/user-profile/page.tsx maps to /user-profile URL. Dynamic routes: [slug]/page.tsx or [...slug]/page.tsx (catch-all). Route groups: (auth)/login/page.tsx - parentheses don't affect URL. Private folders: _components ignored by router. API routes: route.ts in app directory (App Router). 2025 App Router conventions: All routes in app/ directory, special file names required, kebab-case for regular components. Don't: Use .js extension (prefer .ts/.tsx), mix naming styles. Next.js enforces some conventions via routing - follow framework expectations for best results.