Complete Guide to Setting Up NX + Next.js + Expo Project: Modern Monorepo Architecture. Part 1

Complete Guide to Setting Up NX + Next.js + Expo: Modern Cross-Platform Monorepo

Building modern applications often requires managing multiple platforms simultaneously: web applications, mobile apps, and backend APIs. NX provides the perfect foundation for this challenge by offering a monorepo architecture that enables code sharing, consistent tooling, and optimized build processes across web, mobile, and server applications. This comprehensive guide will walk you through creating a production-ready NX workspace that combines Next.js for web development and Expo for cross-platform mobile apps.

What You'll Master

NX workspace creation with cross-platform architecture Next.js application setup with App Router and Tailwind CSS NestJS API configuration for backend services Expo mobile app integration with React Native 0.79+ Optimal project structure for web and mobile code sharing Development workflow optimization and package management

Why Choose NX for Cross-Platform Development?

The combination of NX with Next.js and Expo addresses the fundamental challenge of modern app development: building for multiple platforms without duplicating business logic, UI components, or development processes. NX provides intelligent dependency management, affected change detection, and build optimization, while Next.js delivers server-side rendering and modern React features for web, and Expo enables native mobile development with React Native.

NX version 21+ introduces enhanced cross-platform support with improved task inference and better dependency graph visualization. This reduces configuration overhead while maintaining complete control over your build pipeline.

Prerequisites and Installation

Before creating your cross-platform NX workspace, ensure you have the following tools installed:

bash # Check Node.js version (18+ recommended) node --version # Check npm version npm --version # Install NX CLI globally for workspace management npm i -g nx

For mobile development, you'll also need:

  • iOS Development: Xcode (macOS only)
  • Android Development: Android Studio and Android SDK
  • Expo CLI: Will be installed automatically with the Expo plugin

Creating Your Cross-Platform NX Workspace

Step 1: Initialize Empty NX Workspace

Start with an empty workspace to have complete control over the architecture and gradually add applications as needed:

bash # Create empty workspace with maximum flexibility npx create-nx-workspace@latest

Follow the interactive prompts:

  • Where would you like to create your workspace?monorepo-heaven
  • Which stack do you want to use?none
  • Would you like to use Prettier for code formatting?Yes
  • Which CI provider would you like to use?skip
  • Would you like remote caching to make your build faster?skip

Step 2: Clean Up Package Configuration

After workspace creation, remove the default workspaces configuration that may conflict with NX's dependency management:

json { "name": "monorepo-heaven", "version": "0.0.0", "license": "MIT", "scripts": {}, "private": true, "devDependencies": { "nx": "21.3.2", "prettier": "^2.6.2" } // Remove this section: // "workspaces": [ // "packages/*" // ] }

This ensures NX handles all workspace management and prevents package resolution conflicts.

Setting Up Next.js Web Application

Installing Next.js Plugin

Add Next.js support to your NX workspace with the official plugin:

bash # Navigate to your workspace cd monorepo-heaven # Add Next.js plugin nx add @nx/next # Generate Next.js application nx g @nx/next:app apps/web

Configure your Next.js application with these options:

  • Which stylesheet format would you like to use?tailwind
  • Which linter would you like to use?eslint
  • What unit test runner should be used?none
  • Which E2E test runner would you like to use?none
  • Would you like to use the App Router (recommended)?true
  • Would you like to use src/ directory?true

Understanding the Generated Web Structure

After generation, your Next.js application will have a modern structure optimized for the App Router:

bash apps/web/ ├── src/ │ └── app/ │ ├── globals.css # Global styles with Tailwind │ ├── layout.tsx # Root layout component │ └── page.tsx # Home page component ├── public/ # Static assets ├── next.config.js # Next.js configuration with NX integration ├── tailwind.config.js # Tailwind CSS configuration ├── tsconfig.json # TypeScript configuration └── project.json # NX project configuration

This structure provides the minimum viable setup for a production-ready Next.js application within an NX workspace.

Setting Up NestJS Backend API

Installing NestJS Plugin

Add robust backend API capabilities with NestJS:

bash # Add NestJS plugin nx add @nx/nest # Generate NestJS application nx g @nx/nest:application apps/api

Configure your API with these selections:

  • Which linter would you like to use?eslint
  • Which unit test runner would you like to use?none

Critical NestJS Configuration Fix

The generated NestJS configuration requires a module system update for proper compatibility:

json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "module": "NodeNext", "types": ["node"], "experimentalDecorators": true, "emitDecoratorMetadata": true, "target": "es2021" }, "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], "include": ["src/**/*"] }

The key change is setting "module": "NodeNext" which ensures proper ES module compatibility and prevents runtime errors.

API Structure Overview

Your NestJS API will be generated with a clean, scalable structure:

bash apps/api/ ├── src/ │ ├── app/ │ │ ├── app.controller.ts # Main API controller │ │ ├── app.module.ts # Root application module │ │ └── app.service.ts # Business logic service │ └── main.ts # Application bootstrap ├── tsconfig.app.json # TypeScript configuration ├── tsconfig.json # Base TypeScript config └── project.json # NX project configuration

Setting Up Expo Mobile Application

Installing Expo Plugin

Add cross-platform mobile development capabilities:

bash # Add Expo plugin for React Native development nx add @nx/expo # Generate Expo application nx g @nx/expo:app apps/mobile

Essential Expo Configuration

Update your Expo configuration to enable modern React Native features:

json { "expo": { "name": "Mobile", "slug": "mobile", "version": "1.0.0", "orientation": "portrait", "newArchEnabled": true, "icon": "./assets/icon.png", "userInterfaceStyle": "light", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#ffffff" }, "assetBundlePatterns": [ "**/*" ], "ios": { "supportsTablet": true }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#FFFFFF" } }, "web": { "favicon": "./assets/favicon.png" } } }

The critical addition is "newArchEnabled": true, which enables React Native's New Architecture for better performance and future compatibility.

Updating React Native Dependencies

The default NX Expo template includes outdated React Native packages. Update to the latest stable versions for optimal performance and compatibility:

json { "dependencies": { "react-native": "0.79.2", "react-native-svg": "15.11.2", "expo": "^53.0.9", "expo-splash-screen": "~0.30.8", "expo-status-bar": "~2.2.3" }, "devDependencies": { "react-native-svg-transformer": "1.3.0", "@expo/metro-config": "~0.20.0", "@expo/metro-runtime": "~5.0.4" } }

After updating dependencies, run the installation:

bash # Install updated dependencies npm install # Clear any existing caches npx nx reset

Mobile Application Structure

Your Expo application will have a structure optimized for cross-platform development:

bash apps/mobile/ ├── src/ │ └── app/ │ └── App.tsx # Main mobile application component ├── assets/ # Mobile-specific assets ├── app.json # Expo configuration ├── metro.config.js # Metro bundler configuration ├── tsconfig.json # TypeScript configuration └── project.json # NX project configuration

Optimizing Development Workflow

Adding Convenient NPM Scripts

Enhance your development experience by adding convenient scripts to your root package.json:

json { "name": "monorepo-heaven", "version": "0.0.0", "license": "MIT", "scripts": { "web:dev": "npx nx dev web", "api:dev": "npx nx serve api", "mobile:dev": "npx nx serve mobile", "build:all": "npx nx run-many --target=build --all", "lint:all": "npx nx run-many --target=lint --all", "graph": "npx nx graph" }, "private": true, "devDependencies": { "nx": "21.3.2", "prettier": "^2.6.2" } }

Now you can run your applications with simple commands:

bash # Start web development server yarn web:dev # Start API development server yarn api:dev # Start mobile development with Expo yarn mobile:dev # Build all applications yarn build:all # View dependency graph yarn graph

Understanding Your Workspace Structure

Your completed NX workspace now has a clean, scalable architecture:

bash monorepo-heaven/ ├── apps/ │ ├── web/ # Next.js web application │ ├── api/ # NestJS backend API │ └── mobile/ # Expo mobile application ├── libs/ # Shared libraries (empty for now) ├── tools/ # Custom scripts and configurations ├── nx.json # NX workspace configuration ├── package.json # Root package management └── tsconfig.base.json # Base TypeScript configuration

Development Commands Reference

Application-Specific Commands

bash # Web application commands nx dev web # Start development server nx build web # Build for production nx lint web # Lint web application # API application commands nx serve api # Start API development server nx build api # Build API for production nx lint api # Lint API code # Mobile application commands nx serve mobile # Start Expo development server nx build mobile # Build mobile application nx lint mobile # Lint mobile code

Workspace-Wide Commands

bash # Multi-project operations nx run-many --target=build --all # Build all applications nx run-many --target=lint --all # Lint all projects nx affected:build # Build only affected projects nx affected:test # Test only affected projects # Workspace management nx graph # View dependency graph nx reset # Clear NX cache nx report # Generate workspace report

Next Steps and Architecture Planning

With your foundational NX workspace established, you're ready to build scalable cross-platform applications. The next phase involves:

Immediate Development Tasks

  • Create shared component libraries for code reuse between web and mobile
  • Implement API endpoints in your NestJS application
  • Set up state management and data fetching patterns
  • Configure shared TypeScript types across all applications

Advanced Architecture Considerations

  • Design a shared UI component system that works across platforms
  • Implement authentication and authorization across web and mobile
  • Set up shared business logic libraries
  • Configure deployment pipelines for each platform
Start simple with your shared libraries. Begin by extracting common TypeScript interfaces and utility functions before moving to complex component sharing between web and mobile platforms.

Troubleshooting Common Setup Issues

React Native Version Conflicts

If you encounter React Native version issues:

bash # Clear all caches and reinstall rm -rf node_modules npm cache clean --force npm install # Reset NX cache npx nx reset

Metro Bundler Configuration

For mobile development issues, ensure your Metro config is properly set up:

javascript const { getDefaultConfig } = require('@expo/metro-config'); const { withNxMetro } = require('@nx/expo'); const defaultConfig = getDefaultConfig(__dirname); module.exports = withNxMetro(defaultConfig, { // NX configuration options watchFolders: [], });

TypeScript Configuration Issues

Ensure all applications can resolve shared types by verifying your base TypeScript configuration:

json { "compileOnSave": false, "compilerOptions": { "rootDir": ".", "sourceMap": true, "declaration": false, "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, "target": "es2015", "module": "esnext", "lib": ["es2020", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, "baseUrl": ".", "paths": {} }, "exclude": ["node_modules", "tmp"] }

Conclusion

You've successfully created a powerful foundation for cross-platform development using NX, Next.js, and Expo. This setup provides a scalable monorepo architecture that enables code sharing between web and mobile applications while maintaining the flexibility to optimize each platform independently.

The workspace you've built includes modern development tools, optimized build processes, and a clean project structure that will grow with your application's complexity. With Next.js powering your web experience, NestJS handling your backend API, and Expo enabling cross-platform mobile development, you have all the essential pieces for building comprehensive modern applications.

In the next part of this series, we'll dive deep into creating shared libraries, implementing cross-platform components, and establishing data flow patterns that maximize code reuse while maintaining platform-specific optimizations. The foundation you've established here will serve as the launching point for building sophisticated, maintainable applications that deliver exceptional user experiences across all platforms.


This post was originally published on make-it.run.

0
Subscribe to my newsletter

Read articles from Devops Make It Run directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Devops Make It Run
Devops Make It Run