├── src ├── modules │ └── home │ │ ├── index.ts │ │ ├── Home.module.css │ │ └── Home.tsx ├── common │ ├── components │ │ └── marketing │ │ │ ├── Card │ │ │ ├── index.ts │ │ │ ├── Card.tsx │ │ │ └── Card.module.css │ │ │ ├── Footer │ │ │ ├── index.ts │ │ │ ├── Footer.module.css │ │ │ └── Footer.tsx │ │ │ └── SimpleGrid │ │ │ ├── index.ts │ │ │ ├── SimpleGrid.tsx │ │ │ └── SimpleGrid.module.css │ └── styles │ │ └── globals.css └── pages │ ├── _app.tsx │ ├── api │ └── hello.ts │ └── index.tsx ├── .eslintrc.json ├── next.config.js ├── public ├── favicon.ico └── vercel.svg ├── next-env.d.ts ├── README.md ├── package.json ├── .gitignore └── tsconfig.json /src/modules/home/index.ts: -------------------------------------------------------------------------------- 1 | export { Home } from "./Home"; 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /src/common/components/marketing/Card/index.ts: -------------------------------------------------------------------------------- 1 | export { Card } from "./Card"; 2 | -------------------------------------------------------------------------------- /src/common/components/marketing/Footer/index.ts: -------------------------------------------------------------------------------- 1 | export { Footer } from "./Footer"; 2 | -------------------------------------------------------------------------------- /src/common/components/marketing/SimpleGrid/index.ts: -------------------------------------------------------------------------------- 1 | export { SimpleGrid } from "./SimpleGrid"; 2 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | } 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okeeffed/nextjs-enterprise-project-structure/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@styles/globals.css"; 2 | import type { AppProps } from "next/app"; 3 | 4 | function MyApp({ Component, pageProps }: AppProps) { 5 | return ; 6 | } 7 | 8 | export default MyApp; 9 | -------------------------------------------------------------------------------- /src/common/components/marketing/SimpleGrid/SimpleGrid.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import styles from "./SimpleGrid.module.css"; 3 | 4 | export const SimpleGrid: React.FC = ({ children }) => { 5 | return
{children}
; 6 | }; 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Enterprise Project Structure 2 | 3 | This repo goes along with the [blog post on my website](https://blog.dennisokeeffe.com/blog/2021-12-06-nextjs-enterprise-project-structure). 4 | 5 | ## Getting started 6 | 7 | ```s 8 | $ npm run dev 9 | ``` 10 | -------------------------------------------------------------------------------- /src/common/components/marketing/SimpleGrid/SimpleGrid.module.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-wrap: wrap; 6 | max-width: 800px; 7 | } 8 | 9 | @media (max-width: 600px) { 10 | .grid { 11 | width: 100%; 12 | flex-direction: column; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/common/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /src/common/components/marketing/Footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | display: flex; 3 | flex: 1; 4 | padding: 2rem 0; 5 | border-top: 1px solid #eaeaea; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .footer a { 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | flex-grow: 1; 15 | } 16 | 17 | .logo { 18 | height: 1em; 19 | margin-left: 0.5rem; 20 | } 21 | -------------------------------------------------------------------------------- /src/common/components/marketing/Card/Card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import styles from "./Card.module.css"; 3 | 4 | interface CardProps { 5 | href: string; 6 | title: string; 7 | body: string; 8 | } 9 | 10 | export const Card: React.FC = ({ href, title, body }) => { 11 | return ( 12 | 13 |

{title}

14 |

{body}

15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-enterprise-project-structure", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "next": "12.0.7", 12 | "react": "17.0.2", 13 | "react-dom": "17.0.2" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "16.11.11", 17 | "@types/react": "17.0.37", 18 | "eslint": "8.4.0", 19 | "eslint-config-next": "12.0.7", 20 | "typescript": "4.5.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /src/common/components/marketing/Card/Card.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | margin: 1rem; 3 | padding: 1.5rem; 4 | text-align: left; 5 | color: inherit; 6 | text-decoration: none; 7 | border: 1px solid #eaeaea; 8 | border-radius: 10px; 9 | transition: color 0.15s ease, border-color 0.15s ease; 10 | max-width: 300px; 11 | } 12 | 13 | .card:hover, 14 | .card:focus, 15 | .card:active { 16 | color: #0070f3; 17 | border-color: #0070f3; 18 | } 19 | 20 | .card h2 { 21 | margin: 0 0 1rem 0; 22 | font-size: 1.5rem; 23 | } 24 | 25 | .card p { 26 | margin: 0; 27 | font-size: 1.25rem; 28 | line-height: 1.5; 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { NextPage } from "next"; 2 | import Head from "next/head"; 3 | import { Home } from "@modules/home"; 4 | import { Footer } from "@components/marketing/Footer"; 5 | 6 | const IndexPage: NextPage = () => { 7 | return ( 8 |
13 | 14 | Create Next App 15 | 16 | 17 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default IndexPage; 25 | -------------------------------------------------------------------------------- /src/common/components/marketing/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Image from "next/image"; 3 | import styles from "./Footer.module.css"; 4 | 5 | export const Footer: React.FC = () => { 6 | return ( 7 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@components/*": ["src/common/components/*"], 6 | "@styles/*": ["src/common/styles/*"], 7 | "@modules/*": ["src/modules/*"] 8 | }, 9 | "target": "es5", 10 | "lib": ["dom", "dom.iterable", "esnext"], 11 | "allowJs": true, 12 | "skipLibCheck": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "jsx": "preserve", 22 | "incremental": true 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /src/modules/home/Home.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | min-height: 100vh; 3 | padding: 4rem 0; 4 | flex: 1; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | } 10 | 11 | .title a { 12 | color: #0070f3; 13 | text-decoration: none; 14 | } 15 | 16 | .title a:hover, 17 | .title a:focus, 18 | .title a:active { 19 | text-decoration: underline; 20 | } 21 | 22 | .title { 23 | margin: 0; 24 | line-height: 1.15; 25 | font-size: 4rem; 26 | } 27 | 28 | .title, 29 | .description { 30 | text-align: center; 31 | } 32 | 33 | .description { 34 | margin: 4rem 0; 35 | line-height: 1.5; 36 | font-size: 1.5rem; 37 | } 38 | 39 | .code { 40 | background: #fafafa; 41 | border-radius: 5px; 42 | padding: 0.75rem; 43 | font-size: 1.1rem; 44 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 45 | Bitstream Vera Sans Mono, Courier New, monospace; 46 | } 47 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/modules/home/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Card } from "@components/marketing/Card"; 3 | import { SimpleGrid } from "@components/marketing/SimpleGrid"; 4 | import styles from "./Home.module.css"; 5 | 6 | export const Home: React.FC = () => { 7 | return ( 8 |
9 |

10 | Welcome to Next.js! 11 |

12 | 13 |

14 | Get started by editing{" "} 15 | pages/index.tsx 16 |

17 | 18 | 19 | 24 | 29 | 34 | 39 | 40 |
41 | ); 42 | }; 43 | --------------------------------------------------------------------------------