├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── (custom) │ ├── layout.js │ └── newsletter │ │ └── page.js ├── (primary) │ ├── dashboard │ │ ├── account │ │ │ └── page.js │ │ ├── analytics │ │ │ └── page.js │ │ ├── layout.js │ │ └── settings │ │ │ ├── @notifications │ │ │ ├── page.js │ │ │ └── users │ │ │ │ └── page.js │ │ │ ├── @revenue │ │ │ ├── default.js │ │ │ ├── loading.js │ │ │ └── page.js │ │ │ ├── default.js │ │ │ ├── error.js │ │ │ ├── layout.js │ │ │ ├── loading.js │ │ │ └── page.js │ ├── layout.js │ └── page.js ├── favicon.ico ├── globals.css └── layout.js ├── components ├── DashboardNavLink.js ├── Footer.js ├── Header.js ├── HeaderNavLink.js ├── NewsletterFooter.js └── SearchField.js ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── next.svg └── vercel.svg └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.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 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /app/(custom)/layout.js: -------------------------------------------------------------------------------- 1 | import NewsletterFooter from '@/components/NewsletterFooter'; 2 | 3 | export default function CustomLayout({ children }) { 4 | return ( 5 |
6 |
7 |
{children}
8 |
9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /app/(custom)/newsletter/page.js: -------------------------------------------------------------------------------- 1 | export const NewsletterPage = () => { 2 | return ( 3 |
4 |

5 | Get our Weekly Developer's Tips via Email 6 |

7 |
8 | 14 | 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default NewsletterPage; 26 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/account/page.js: -------------------------------------------------------------------------------- 1 | const AccountPage = () => { 2 | return
Account screen
; 3 | }; 4 | 5 | export default AccountPage; 6 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/analytics/page.js: -------------------------------------------------------------------------------- 1 | const Page = () => { 2 | return
Analytics here...
; 3 | }; 4 | 5 | export default Page; 6 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/layout.js: -------------------------------------------------------------------------------- 1 | import DashboardNavLink from '@/components/DashboardNavLink'; 2 | 3 | const DashboardLayout = ({ children }) => { 4 | return ( 5 |
6 | 32 |
33 | {children} 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default DashboardLayout; 40 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/@notifications/page.js: -------------------------------------------------------------------------------- 1 | export default async function Notifications() { 2 | await new Promise((resolve) => setTimeout(resolve, 3000)); 3 | return

Notifications

; 4 | } 5 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/@notifications/users/page.js: -------------------------------------------------------------------------------- 1 | export default async function Users() { 2 | await new Promise((resolve) => setTimeout(resolve, 3000)); 3 | return

users notifications

; 4 | } 5 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/@revenue/default.js: -------------------------------------------------------------------------------- 1 | export default function RevenueDefault() { 2 | return

Revenue default page

; 3 | } 4 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/@revenue/loading.js: -------------------------------------------------------------------------------- 1 | export default function LoadingRevenue() { 2 | return

Loading revenue...

; 3 | } 4 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/@revenue/page.js: -------------------------------------------------------------------------------- 1 | export default async function Revenue() { 2 | await new Promise((resolve) => setTimeout(resolve, 5000)); 3 | // throw new Error(); 4 | return

Revenue metrics

; 5 | } 6 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/default.js: -------------------------------------------------------------------------------- 1 | export default function SettingsDefault() { 2 | return

Settings default page

; 3 | } 4 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/error.js: -------------------------------------------------------------------------------- 1 | 'use client'; // Error components must be Client Components 2 | 3 | export default function Error() { 4 | return ( 5 |
6 |

Something went wrong!

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/layout.js: -------------------------------------------------------------------------------- 1 | const SettingsPageLayout = ({ children, notifications, revenue }) => { 2 | return ( 3 | <> 4 | {children} 5 |
6 |
7 | {notifications} 8 |
9 | 10 |
11 | {revenue} 12 |
13 |
14 | 15 | ); 16 | }; 17 | 18 | export default SettingsPageLayout; 19 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/loading.js: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return

Loading...

; 3 | } 4 | -------------------------------------------------------------------------------- /app/(primary)/dashboard/settings/page.js: -------------------------------------------------------------------------------- 1 | const SettingsPage = async () => { 2 | await new Promise((resolve) => setTimeout(resolve, 2000)); 3 | return

Settings page

; 4 | }; 5 | 6 | export default SettingsPage; 7 | -------------------------------------------------------------------------------- /app/(primary)/layout.js: -------------------------------------------------------------------------------- 1 | import Header from '@/components/Header'; 2 | import Footer from '@/components/Footer'; 3 | 4 | export default function MainLayout({ children }) { 5 | return ( 6 |
7 |
8 |
9 |
{children}
10 |
11 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /app/(primary)/page.js: -------------------------------------------------------------------------------- 1 | const Home = () => { 2 | return ( 3 |
4 |

5 | Demo project 6 |

7 |

8 | Nested layouts in Next.js 9 |

10 |
11 | ); 12 | }; 13 | 14 | export default Home; 15 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ibaslogic/nextjs-nested-layout-app-dir/fca22424b8804c8dd3716dfe35c81c703d5b88ab/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: #464646; 7 | } 8 | 9 | body { 10 | color: rgb(var(--foreground-rgb)); 11 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 12 | sans-serif; 13 | } 14 | 15 | @layer base { 16 | h2 { 17 | @apply text-base font-semibold mb-3; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | import './globals.css'; 2 | 3 | export const metadata = { 4 | title: 'Create Next App', 5 | description: 'Generated by create next app', 6 | }; 7 | 8 | export default function RootLayout({ children }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /components/DashboardNavLink.js: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Link from 'next/link'; 4 | import { usePathname } from 'next/navigation'; 5 | 6 | const DashboardNavLink = ({ href, children }) => { 7 | const pathname = usePathname(); 8 | const active = href === pathname; 9 | 10 | return ( 11 | 17 | {children} 18 | 19 | ); 20 | }; 21 | 22 | export default DashboardNavLink; 23 | -------------------------------------------------------------------------------- /components/Footer.js: -------------------------------------------------------------------------------- 1 | const Footer = () => { 2 | return ( 3 | 6 | ); 7 | }; 8 | 9 | export default Footer; 10 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import Link from 'next/link'; 3 | import HeaderNavLink from './HeaderNavLink'; 4 | import SearchField from './SearchField'; 5 | 6 | const menuItems = [ 7 | { label: `Home`, url: `/` }, 8 | { label: `Dashboard`, url: `/dashboard/analytics` }, 9 | { label: `Newsletter`, url: `/newsletter` }, 10 | ]; 11 | 12 | const Header = () => { 13 | return ( 14 |
15 |
16 | 17 | logo 24 | 25 | 34 |
35 | 36 | 37 |
38 | ); 39 | }; 40 | 41 | export default Header; 42 | -------------------------------------------------------------------------------- /components/HeaderNavLink.js: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Link from 'next/link'; 4 | import { usePathname } from 'next/navigation'; 5 | 6 | const HeaderNavLink = ({ href, children }) => { 7 | const pathname = usePathname(); 8 | const active = href === pathname; 9 | 10 | return ( 11 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default HeaderNavLink; 27 | -------------------------------------------------------------------------------- /components/NewsletterFooter.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import Link from 'next/link'; 3 | 4 | const NewsletterFooter = () => { 5 | return ( 6 | 22 | ); 23 | }; 24 | 25 | export default NewsletterFooter; 26 | -------------------------------------------------------------------------------- /components/SearchField.js: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState } from 'react'; 4 | 5 | const SearchField = () => { 6 | const [search, setSearch] = useState(''); 7 | 8 | return ( 9 | setSearch(e.target.value)} 13 | className="block w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:border-gray-500" 14 | placeholder="Search..." 15 | /> 16 | ); 17 | }; 18 | 19 | export default SearchField; 20 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-layout-app-dir", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "autoprefixer": "10.4.14", 13 | "eslint": "8.40.0", 14 | "eslint-config-next": "13.4.1", 15 | "next": "^14.1.0", 16 | "postcss": "8.4.23", 17 | "react": "18.2.0", 18 | "react-dom": "18.2.0", 19 | "tailwindcss": "3.3.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | --------------------------------------------------------------------------------