├── .eslintignore ├── .vscode ├── settings.json └── tsreact.code-snippets ├── css └── tailwind.css ├── commitlint.config.js ├── public ├── favicon │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ └── site.webmanifest ├── robots.txt ├── sitemap.xml └── sitemap-0.xml ├── .env.example ├── .husky ├── pre-commit └── commit-msg ├── postcss.config.js ├── .prettierignore ├── renovate.json ├── next.config.js ├── src ├── lib │ ├── __test__ │ │ └── utils.test.ts │ └── utils.ts ├── constant │ ├── app-config.ts │ └── theme.ts ├── layouts │ └── container.tsx └── components │ ├── icons │ └── index.tsx │ ├── meta │ └── index.tsx │ └── theme-selector │ └── index.tsx ├── next-sitemap.config.js ├── pages ├── _app.tsx ├── api │ └── hello.ts ├── index.tsx └── _document.tsx ├── next-env.d.ts ├── vitest.config.ts ├── prettier.config.js ├── .lintstagedrc.js ├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── nextjs_bundle_analysis.yml ├── tsconfig.json ├── tailwind.config.js ├── .eslintrc.js ├── README.md └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/favicon.ico -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # place your url for generate sitemap after build 2 | process.env.NEXT_PUBLIC_SITE_URL= -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm lint-staged 5 | 6 | -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiqryq/kit/HEAD/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | .cache 3 | package-lock.json 4 | public 5 | node_modules 6 | next-env.d.ts 7 | next.config.ts 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | }; 6 | 7 | module.exports = nextConfig; 8 | -------------------------------------------------------------------------------- /src/lib/__test__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import { cn } from '../utils'; 3 | 4 | test(cn('bg-white-500', 'bg-red-500'), () => { 5 | expect('bg-red-500'); 6 | }); 7 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # * 2 | User-agent: * 3 | Allow: / 4 | 5 | # Host 6 | Host: https://nextboilerplate.fiqry.dev/ 7 | 8 | # Sitemaps 9 | Sitemap: https://nextboilerplate.fiqry.dev/sitemap.xml 10 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /next-sitemap.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next-sitemap').IConfig} */ 2 | module.exports = { 3 | siteUrl: process.env.NEXT_PUBLIC_SITE_URL, 4 | generateRobotsTxt: true, 5 | changefreq: 'daily', 6 | priority: 0.7 7 | }; 8 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app'; 2 | import 'css/tailwind.css'; 3 | 4 | function MyApp({ Component, pageProps }: AppProps) { 5 | return ; 6 | } 7 | 8 | export default MyApp; 9 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://nextboilerplate.fiqry.dev/sitemap-0.xml 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | import react from '@vitejs/plugin-react'; 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | test: { 7 | environment: 'jsdom' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/constant/app-config.ts: -------------------------------------------------------------------------------- 1 | export const AppConfig = { 2 | site_name: 'next-boilerplate', 3 | title: 'next-boilerplate', 4 | description: 5 | 'simple nextjs boilerplate with tailwindcss + typescript ⚡️ for fast development.', 6 | locale: 'en' 7 | }; 8 | -------------------------------------------------------------------------------- /public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /src/constant/theme.ts: -------------------------------------------------------------------------------- 1 | import { LightIcon, DarkIcon, SystemIcon } from '@/components/icons'; 2 | 3 | export const themes = [ 4 | { name: 'Light', value: 'light', icon: LightIcon }, 5 | { name: 'Dark', value: 'dark', icon: DarkIcon }, 6 | { name: 'System', value: 'system', icon: SystemIcon } 7 | ]; 8 | -------------------------------------------------------------------------------- /src/layouts/container.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface Props { 4 | children: React.ReactNode; 5 | } 6 | 7 | const Container: React.FC = ({ children }) => { 8 | return
{children}
; 9 | }; 10 | 11 | export default Container; 12 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'none', 4 | arrowParens: 'avoid', 5 | proseWrap: 'preserve', 6 | quoteProps: 'as-needed', 7 | bracketSameLine: false, 8 | bracketSpacing: true, 9 | tabWidth: 2, 10 | plugins: [require('prettier-plugin-tailwindcss')] 11 | }; 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Type check TypeScript files 3 | '**/*.(ts|tsx)': () => 'pnpm tsc --noEmit', 4 | 5 | // Lint & Prettify TS and JS files 6 | '**/*.(ts|tsx|js)': filenames => [ 7 | 'pnpm test:all', 8 | 'pnpm lint', 9 | `pnpm prettier --write ${filenames.join(' ')}` 10 | ], 11 | 12 | // Prettify only Markdown and JSON files 13 | '**/*.(md|json)': filenames => `pnpm prettier --write ${filenames.join(' ')}` 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /.pnp 5 | .pnp.js 6 | /node_modules 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 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fiqryq 2 | patreon: # Replace with a single Patreon username 3 | open_collective: # Replace with a single Open Collective username 4 | ko_fi: fiqryq 5 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 6 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 7 | liberapay: # Replace with a single Liberapay username 8 | issuehunt: # Replace with a single IssueHunt username 9 | otechie: # Replace with a single Otechie username 10 | custom: https://saweria.co/fiqryq 11 | -------------------------------------------------------------------------------- /public/sitemap-0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://nextboilerplate.fiqry.dev2022-12-20T01:40:24.664Zdaily0.7 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "forceConsistentCasingInFileNames": true, 8 | "noEmit": true, 9 | "incremental": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "baseUrl": ".", 17 | "paths": { 18 | "@/*": ["./src/*"], 19 | "@/public/*": ["./public/*"] 20 | } 21 | }, 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "commitlint.config.js"], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@/components/meta'; 2 | import ThemeSelector from '@/components/theme-selector'; 3 | 4 | import type { NextPage } from 'next'; 5 | 6 | const Home: NextPage = () => { 7 | return ( 8 | <> 9 | 13 |
14 |
15 |
    16 |
  • 17 | 18 |
  • 19 |
20 |
21 |
22 | 23 | ); 24 | }; 25 | 26 | export default Home; 27 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx}', 6 | './layouts/**/*.{js,ts,jsx,tsx}', 7 | './src/**/*.{js,ts,jsx,tsx}' 8 | ], 9 | darkMode: 'class', 10 | theme: { 11 | extend: { 12 | aspectRatio: { 13 | auto: 'auto', 14 | square: '1 / 1', 15 | video: '16 / 9', 16 | 1: '1', 17 | 2: '2', 18 | 3: '3', 19 | 4: '4', 20 | 5: '5', 21 | 6: '6', 22 | 7: '7', 23 | 8: '8', 24 | 9: '9', 25 | 10: '10', 26 | 11: '11', 27 | 12: '12', 28 | 13: '13', 29 | 14: '14', 30 | 15: '15', 31 | 16: '16' 32 | } 33 | } 34 | }, 35 | corePlugins: { 36 | aspectRatio: false 37 | }, 38 | plugins: [ 39 | require('@tailwindcss/forms'), 40 | require('@tailwindcss/typography'), 41 | require('@tailwindcss/aspect-ratio'), 42 | require('@tailwindcss/container-queries') 43 | ] 44 | }; 45 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | env: { 5 | browser: true, 6 | amd: true, 7 | node: true, 8 | es6: true 9 | }, 10 | plugins: ['@typescript-eslint', 'prettier'], 11 | extends: [ 12 | 'eslint:recommended', 13 | 'plugin:@typescript-eslint/eslint-recommended', 14 | 'plugin:@typescript-eslint/recommended', 15 | 'plugin:jsx-a11y/recommended', 16 | 'prettier', 17 | 'next', 18 | 'next/core-web-vitals' 19 | ], 20 | rules: { 21 | 'react/react-in-jsx-scope': 'off', 22 | 'jsx-a11y/anchor-is-valid': [ 23 | 'error', 24 | { 25 | components: ['Link'], 26 | specialLink: ['hrefLeft', 'hrefRight'], 27 | aspects: ['invalidHref', 'preferButton'] 28 | } 29 | ], 30 | 'react/prop-types': 0, 31 | 'no-unused-vars': 0, 32 | 'prettier/prettier': 'warn', 33 | 'no-console': 'warn', 34 | 'react/no-unescaped-entities': 'off', 35 | '@typescript-eslint/explicit-module-boundary-types': 'off', 36 | '@typescript-eslint/no-var-requires': 'off', 37 | '@typescript-eslint/ban-ts-comment': 'off', 38 | '@typescript-eslint/no-explicit-any': 'off' 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![untitled](https://user-images.githubusercontent.com/25787603/203488615-7b381491-7966-4dbc-abfe-31334130eda3.png) 2 | 3 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ffiqryq%2Fnext-boilerplate) 4 | 5 | ### Getting Started 6 | 7 | create new project : 8 | 9 | ``` 10 | npx create-next-app --example https://github.com/fiqryq/next-boilerplate 11 | ``` 12 | 13 | or use this template and clone then run : 14 | 15 | ```bash 16 | # install dependency 17 | yarn install 18 | # run local server development 19 | yarn run dev 20 | ``` 21 | 22 | ### Features 23 | 24 | - absolute path import. 25 | - integrate with [Tailwind CSS](https://tailwindcss.com/). 26 | - integrate with all tailwind plugins. 27 | - precommit using husky. 28 | - commitlint. 29 | - lint-staged. 30 | - prettier. 31 | - theme selector. 32 | - auto generate sitemap. 33 | - auto update dependencies using [Renovate](https://github.com/renovatebot/renovate). 34 | - analyzes each PR's impact on your next.js app's bundle size and displays it using a commen using [nextjs bundle analysis](https://github.com/hashicorp/nextjs-bundle-analysis). 35 | 36 | ### 🤝 Contributing 37 | 38 | 1. find [issue](https://github.com/fiqryq/next-boilerplate/issues) 39 | 1. fork this repository 40 | 1. create your branch: `git checkout -b new-feature` 41 | 1. commit your changes: `git commit -m 'feat: add some feature (#issue_number)'` 42 | 1. push to the branch: `git push origin new-feature`. 43 | 44 | **After your pull request is merged**, you can safely delete your branch. 45 | 46 | --- 47 | 48 | Made with ♥ by Fiqry choerudin 49 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from 'next/document'; 2 | 3 | const themeScript = ` 4 | let isDarkMode = window.matchMedia('(prefers-color-scheme: dark)') 5 | 6 | function updateTheme(theme) { 7 | theme = theme ?? window.localStorage.theme ?? 'system' 8 | 9 | if (theme === 'dark' || (theme === 'system' && isDarkMode.matches)) { 10 | document.documentElement.classList.add('dark') 11 | } else if (theme === 'light' || (theme === 'system' && !isDarkMode.matches)) { 12 | document.documentElement.classList.remove('dark') 13 | } 14 | 15 | return theme 16 | } 17 | 18 | function updateThemeWithoutTransitions(theme) { 19 | updateTheme(theme) 20 | document.documentElement.classList.add('[&_*]:!transition-none') 21 | window.setTimeout(() => { 22 | document.documentElement.classList.remove('[&_*]:!transition-none') 23 | }, 0) 24 | } 25 | 26 | document.documentElement.setAttribute('data-theme', updateTheme()) 27 | 28 | new MutationObserver(([{ oldValue }]) => { 29 | let newValue = document.documentElement.getAttribute('data-theme') 30 | if (newValue !== oldValue) { 31 | try { 32 | window.localStorage.setItem('theme', newValue) 33 | } catch {} 34 | updateThemeWithoutTransitions(newValue) 35 | } 36 | }).observe(document.documentElement, { attributeFilter: ['data-theme'], attributeOldValue: true }) 37 | 38 | isDarkMode.addEventListener('change', () => updateThemeWithoutTransitions()) 39 | `; 40 | 41 | export default function Document() { 42 | return ( 43 | 44 | 45 |