├── .node-version ├── packages ├── api │ ├── codegen │ │ └── .gitkeep │ ├── .gitignore │ ├── kubb.config.ts │ ├── package.json │ ├── base.config.ts │ └── axios.client.ts ├── design-tokens │ ├── json │ │ └── .gitkeep │ ├── package.json │ └── tokens.config.json ├── core │ ├── tabs │ │ ├── index.ts │ │ ├── tabs.stories.tsx │ │ ├── tabs.test.tsx │ │ └── tabs.tsx │ ├── button │ │ ├── index.ts │ │ ├── button.stories.tsx │ │ ├── button.test.tsx │ │ └── button.tsx │ ├── input │ │ ├── index.ts │ │ ├── input.stories.tsx │ │ ├── input.tsx │ │ └── input.test.tsx │ ├── label │ │ ├── index.ts │ │ ├── label.stories.tsx │ │ ├── label.test.tsx │ │ └── label.tsx │ ├── switch │ │ ├── index.ts │ │ ├── switch.stories.tsx │ │ ├── switch.test.tsx │ │ └── switch.tsx │ ├── tooltip │ │ ├── index.ts │ │ ├── tooltip.stories.tsx │ │ ├── tooltip.tsx │ │ └── tooltip.test.tsx │ ├── checkbox │ │ ├── index.ts │ │ ├── checkbox.stories.tsx │ │ ├── checkbox.test.tsx │ │ └── checkbox.tsx │ ├── skeleton │ │ ├── index.ts │ │ ├── skeleton.tsx │ │ ├── skeleton.test.tsx │ │ └── skeleton.stories.tsx │ ├── textarea │ │ ├── index.ts │ │ ├── textarea.stories.tsx │ │ ├── textarea.tsx │ │ └── textarea.test.tsx │ ├── typography │ │ ├── index.ts │ │ ├── typography.test.tsx │ │ ├── typography.tsx │ │ └── typography.stories.tsx │ ├── radio-group │ │ ├── index.ts │ │ ├── radio-group.stories.tsx │ │ ├── radio-group.tsx │ │ └── radio-group.test.tsx │ ├── tsconfig.json │ ├── cn.ts │ ├── form │ │ ├── index.ts │ │ └── form.tsx │ ├── dialog │ │ ├── index.ts │ │ ├── dialog.stories.tsx │ │ └── dialog.tsx │ ├── select │ │ ├── index.ts │ │ ├── select.stories.tsx │ │ └── select.tsx │ ├── components.json │ ├── sonner │ │ └── index.tsx │ └── package.json ├── routes │ ├── declarative-routing.config.json │ ├── src │ │ ├── index.ts │ │ ├── hooks.ts │ │ ├── utils.ts │ │ └── makeRoute.tsx │ ├── package.json │ └── README.md ├── ts-config │ ├── package.json │ └── base.json ├── metrics │ ├── index.ts │ └── package.json └── logger │ ├── package.json │ └── index.ts ├── bunfig.toml ├── .npmrc ├── .prettierignore ├── app ├── favicon.ico ├── _shared │ └── utilities │ │ ├── error-boundary │ │ ├── index.ts │ │ ├── error-boundary.stories.tsx │ │ ├── error-boundary.test.tsx │ │ └── error-boundary.tsx │ │ └── responsive.tsx ├── (public) │ ├── (home) │ │ ├── page.tsx │ │ ├── error.tsx │ │ └── _components │ │ │ └── hero.tsx │ └── ui-demo │ │ └── page.tsx ├── api │ ├── health │ │ └── route.ts │ ├── ready │ │ └── route.ts │ └── metrics │ │ └── route.ts ├── not-found.tsx ├── sitemap.ts ├── robots.ts ├── manifest.ts ├── global-error.tsx └── layout.tsx ├── src ├── types │ └── global.d.ts ├── fonts │ ├── source │ │ └── geist.woff2 │ └── geist.ts ├── utils │ ├── cn.ts │ └── get-url.ts ├── tests │ └── e2e │ │ └── example.spec.ts ├── env │ ├── client.ts │ └── server.ts ├── hooks │ └── use-responsive.ts └── styles │ └── globals.css ├── knip.jsonc ├── .docker ├── docker-entrypoint.sh └── Dockerfile ├── next-env.d.ts ├── postcss.config.mjs ├── .prettierrc.yml ├── .jscpd.json ├── lefthook.yml ├── middleware.ts ├── tsconfig.json ├── .env.example ├── instrumentation.ts ├── vitest.config.ts ├── instrumentation-client.ts ├── .storybook ├── preview.tsx └── main.ts ├── .gitignore ├── lint-staged.config.mjs ├── README.md ├── eslint.config.mjs ├── playwright.config.ts ├── .husky └── _ │ ├── pre-commit │ └── prepare-commit-msg ├── public └── images │ └── svg │ └── logo.svg ├── next.config.ts ├── headers.ts └── package.json /.node-version: -------------------------------------------------------------------------------- 1 | 22.16 -------------------------------------------------------------------------------- /packages/api/codegen/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/design-tokens/json/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bunfig.toml: -------------------------------------------------------------------------------- 1 | [install] 2 | exact = true 3 | -------------------------------------------------------------------------------- /packages/core/tabs/index.ts: -------------------------------------------------------------------------------- 1 | export { Tabs } from './tabs'; 2 | -------------------------------------------------------------------------------- /packages/core/button/index.ts: -------------------------------------------------------------------------------- 1 | export { Button } from './button'; 2 | -------------------------------------------------------------------------------- /packages/core/input/index.ts: -------------------------------------------------------------------------------- 1 | export { Input } from './input'; 2 | -------------------------------------------------------------------------------- /packages/core/label/index.ts: -------------------------------------------------------------------------------- 1 | export { Label } from './label'; 2 | -------------------------------------------------------------------------------- /packages/core/switch/index.ts: -------------------------------------------------------------------------------- 1 | export { Switch } from './switch'; 2 | -------------------------------------------------------------------------------- /packages/core/tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export { Tooltip } from './tooltip'; 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | engine-strict=true 3 | legacy-peer-deps=true 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | storybook-static 3 | .next 4 | public 5 | -------------------------------------------------------------------------------- /packages/core/checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export { Checkbox } from './checkbox'; 2 | -------------------------------------------------------------------------------- /packages/core/skeleton/index.ts: -------------------------------------------------------------------------------- 1 | export { Skeleton } from './skeleton'; 2 | -------------------------------------------------------------------------------- /packages/core/textarea/index.ts: -------------------------------------------------------------------------------- 1 | export { Textarea } from './textarea'; 2 | -------------------------------------------------------------------------------- /packages/core/typography/index.ts: -------------------------------------------------------------------------------- 1 | export { Typography } from './typography'; 2 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpractik/nextjs-starter/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /packages/core/radio-group/index.ts: -------------------------------------------------------------------------------- 1 | export { RadioGroup, RadioGroupItem } from './radio-group'; 2 | -------------------------------------------------------------------------------- /packages/api/.gitignore: -------------------------------------------------------------------------------- 1 | swagger.yaml 2 | swagger.json 3 | openapi.yaml 4 | openapi.json 5 | bundled.yaml 6 | -------------------------------------------------------------------------------- /src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@total-typescript/ts-reset'; 2 | import 'typed-query-selector/strict'; 3 | -------------------------------------------------------------------------------- /app/_shared/utilities/error-boundary/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorBoundary, ErrorFallback } from './error-boundary'; 2 | -------------------------------------------------------------------------------- /src/fonts/source/geist.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpractik/nextjs-starter/HEAD/src/fonts/source/geist.woff2 -------------------------------------------------------------------------------- /knip.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/knip@3/schema-jsonc.json", 3 | "project": ["**/*.{js,jsx,ts,tsx}"], 4 | } 5 | -------------------------------------------------------------------------------- /packages/routes/declarative-routing.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "nextjs", 3 | "src": "../../app", 4 | "routes": "./src" 5 | } 6 | -------------------------------------------------------------------------------- /app/(public)/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | import { Hero } from './_components/hero'; 2 | 3 | export default function HomePage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /.docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [[ ! -z "$1" ]]; then 5 | echo ${*} 6 | exec ${*} 7 | else 8 | exec node -v 9 | fi 10 | -------------------------------------------------------------------------------- /app/api/health/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | export function GET() { 4 | return NextResponse.json({ message: 'OK' }, { status: 200 }); 5 | } 6 | -------------------------------------------------------------------------------- /app/api/ready/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | export function GET() { 4 | return NextResponse.json({ message: 'OK' }, { status: 200 }); 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/ts-config/base.json", 3 | "compilerOptions": { 4 | "types": ["@vitest/browser/providers/playwright"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // NOTE: This file should not be edited 4 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 5 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}), 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /src/fonts/geist.ts: -------------------------------------------------------------------------------- 1 | import localFont from 'next/font/local'; 2 | 3 | export const geistSans = localFont({ 4 | src: './source/geist.woff2', 5 | variable: '--font-geist-sans', 6 | weight: '100 900', 7 | }); 8 | -------------------------------------------------------------------------------- /packages/core/form/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Form, 3 | FormControl, 4 | FormDescription, 5 | FormField, 6 | FormItem, 7 | FormLabel, 8 | FormMessage, 9 | useFormField, 10 | } from './form'; 11 | -------------------------------------------------------------------------------- /packages/ts-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/ts-config", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "publishConfig": { 7 | "access": "public" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/routes/src/index.ts: -------------------------------------------------------------------------------- 1 | // Automatically generated by declarative-routing, do NOT edit 2 | import { z } from 'zod'; 3 | import { makeGetRoute, makeRoute } from './makeRoute'; 4 | 5 | const defaultInfo = { 6 | search: z.object({}), 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/get-url.ts: -------------------------------------------------------------------------------- 1 | import { headers } from 'next/headers'; 2 | 3 | export async function getURL() { 4 | const headersList = await headers(); 5 | 6 | const url = headersList.get('x-url') ?? ''; 7 | 8 | return new URL(url); 9 | } 10 | -------------------------------------------------------------------------------- /src/tests/e2e/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test'; 2 | 3 | test('example', async ({ page }) => { 4 | await page.goto('http://localhost:3000/'); 5 | 6 | await expect(page.getByRole('heading')).toContainText('Next Starter'); 7 | }); 8 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | arrowParens: avoid 2 | plugins: 3 | - prettier-plugin-tailwindcss 4 | printWidth: 100 5 | singleQuote: true 6 | tabWidth: 4 7 | tailwindFunctions: 8 | - cn 9 | - cva 10 | tailwindStylesheet: ./app/globals.css 11 | trailingComma: es5 12 | -------------------------------------------------------------------------------- /packages/core/dialog/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Dialog, 3 | DialogClose, 4 | DialogContent, 5 | DialogDescription, 6 | DialogFooter, 7 | DialogHeader, 8 | DialogOverlay, 9 | DialogPortal, 10 | DialogTitle, 11 | DialogTrigger, 12 | } from './dialog'; 13 | -------------------------------------------------------------------------------- /app/not-found.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error from 'next/error'; 4 | 5 | export default function NotFound() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/select/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Select, 3 | SelectContent, 4 | SelectGroup, 5 | SelectItem, 6 | SelectLabel, 7 | SelectScrollDownButton, 8 | SelectScrollUpButton, 9 | SelectSeparator, 10 | SelectTrigger, 11 | SelectValue, 12 | } from './select'; 13 | -------------------------------------------------------------------------------- /packages/core/skeleton/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import type { HTMLAttributes } from 'react'; 2 | 3 | import { cn } from '../cn'; 4 | 5 | export function Skeleton({ className, ...props }: HTMLAttributes) { 6 | return
; 7 | } 8 | -------------------------------------------------------------------------------- /packages/metrics/index.ts: -------------------------------------------------------------------------------- 1 | import client from 'prom-client'; 2 | 3 | const { collectDefaultMetrics } = client; 4 | 5 | const { Registry } = client; 6 | 7 | export const register = new Registry(); 8 | 9 | collectDefaultMetrics({ 10 | prefix: process.env.APP_NAME as string, 11 | register, 12 | }); 13 | -------------------------------------------------------------------------------- /.jscpd.json: -------------------------------------------------------------------------------- 1 | { 2 | "threshold": 2, 3 | "reporters": ["html"], 4 | "ignore": [ 5 | "**/node_modules/**", 6 | "**/.next/**", 7 | "**/.git/**", 8 | "**/.github/**", 9 | "**/.gitlab/**", 10 | "**/.docker/**", 11 | "**/.idea/**", 12 | "**/report/**", 13 | "**/*.svg" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | # Refer for explanation to following link: 2 | # https://lefthook.dev/configuration/ 3 | # 4 | pre-commit: 5 | jobs: 6 | - run: npx lint-staged 7 | 8 | prepare-commit-msg: 9 | commands: 10 | commitizen: 11 | interactive: true 12 | run: npx cz --hook 13 | env: 14 | LEFTHOOK: 0 15 | -------------------------------------------------------------------------------- /packages/core/tabs/tabs.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Tabs } from './tabs'; 4 | 5 | const meta: Meta = { 6 | component: Tabs, 7 | title: 'core/Tabs', 8 | }; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = { 15 | args: {}, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/logger", 3 | "version": "1.0.0", 4 | "main": "index.ts", 5 | "keywords": [], 6 | "author": "", 7 | "license": "ISC", 8 | "description": "", 9 | "type": "module", 10 | "exports": { 11 | ".": "./index.ts" 12 | }, 13 | "dependencies": { 14 | "adze": "2.2.5" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/label/label.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Label } from './label'; 4 | 5 | const meta: Meta = { 6 | component: Label, 7 | title: 'core/Label', 8 | }; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = { 15 | args: {}, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/design-tokens/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/design-tokens", 3 | "version": "1.0.0", 4 | "description": "", 5 | "files": [ 6 | "variables.css" 7 | ], 8 | "scripts": { 9 | "build-tokens": "npx style-dictionary build --config ./tokens.config.json" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /packages/metrics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/metrics", 3 | "version": "1.0.0", 4 | "main": "index.ts", 5 | "type": "module", 6 | "exports": { 7 | ".": "./index.ts" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "description": "", 13 | "dependencies": { 14 | "prom-client": "15.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/api/metrics/route.ts: -------------------------------------------------------------------------------- 1 | import { register } from '@repo/metrics'; 2 | import { NextResponse } from 'next/server'; 3 | 4 | export async function GET() { 5 | const metrics = await register.metrics(); 6 | 7 | const newHeaders = new Headers(); 8 | 9 | newHeaders.set('Content-Type', register.contentType); 10 | 11 | return NextResponse.json(metrics, { headers: newHeaders }); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/dialog/dialog.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Dialog } from './dialog'; 4 | 5 | const meta: Meta = { 6 | component: Dialog, 7 | title: 'core/Dialog', 8 | }; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = { 15 | args: {}, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/core/select/select.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { Select } from './select'; 4 | 5 | const meta: Meta = { 6 | component: Select, 7 | title: 'core/Select', 8 | }; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = { 15 | args: {}, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/logger/index.ts: -------------------------------------------------------------------------------- 1 | import adze, { setup } from 'adze'; 2 | 3 | const appName = process.env.APP_NAME as string; 4 | 5 | const store = setup({ 6 | activeLevel: 'info', 7 | format: 'pretty', 8 | }); 9 | 10 | store.addListener('alert', (log: adze) => { 11 | console.info(log); 12 | }); 13 | 14 | const logger = adze.withEmoji.timestamp.ns(appName).seal(); 15 | 16 | export default logger; 17 | -------------------------------------------------------------------------------- /app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from 'next'; 2 | 3 | import { environment } from '#/env/client'; 4 | 5 | export default function sitemap(): MetadataRoute.Sitemap { 6 | return [ 7 | { 8 | changeFrequency: 'yearly', 9 | lastModified: new Date(), 10 | priority: 1, 11 | url: environment.NEXT_PUBLIC_FRONT_URL, 12 | }, 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /packages/design-tokens/tokens.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": ["./json/**/*.json"], 3 | "platforms": { 4 | "css": { 5 | "transformGroup": "css", 6 | "buildPath": "./", 7 | "files": [ 8 | { 9 | "destination": "variables.css", 10 | "format": "css/variables" 11 | } 12 | ] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/tabs/tabs.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import { Tabs } from './tabs'; 5 | 6 | describe('', () => { 7 | it('it should mount', () => { 8 | render(); 9 | 10 | const tabs = screen.getByTestId('Tabs'); 11 | 12 | expect(tabs).toBeInTheDocument(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/core/label/label.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import { Label } from './label'; 5 | 6 | describe('