├── .nvmrc
├── .husky
├── pre-commit
├── pre-push
└── commit-msg
├── .npmrc
├── public
├── _redirects
├── robots.txt
├── assets
│ ├── chakra-ui-logomark-colored.svg
│ ├── vite-logo.svg
│ ├── ts-logo-512.svg
│ ├── react-icon.svg
│ ├── 404 Error-rafiki.svg
│ └── Building blocks-amico.svg
└── favicon.svg
├── src
├── lib
│ ├── utils
│ │ ├── sample.ts
│ │ └── sample.test.ts
│ ├── services
│ │ └── constants.ts
│ ├── styles
│ │ └── theme
│ │ │ └── index.ts
│ ├── components
│ │ └── ui
│ │ │ ├── provider.tsx
│ │ │ ├── button.tsx
│ │ │ └── color-mode.tsx
│ ├── pages
│ │ ├── 404
│ │ │ └── index.tsx
│ │ └── home
│ │ │ ├── index.tsx
│ │ │ └── components
│ │ │ ├── some-text.tsx
│ │ │ ├── some-image.tsx
│ │ │ └── cta-section.tsx
│ └── layout
│ │ ├── components
│ │ ├── header.tsx
│ │ ├── footer.tsx
│ │ └── meta.tsx
│ │ └── index.tsx
├── routes
│ ├── index.tsx
│ └── __root.tsx
├── env.d.ts
├── main.tsx
└── routeTree.gen.ts
├── vercel.json
├── .vscode
├── settings.template.json
└── extensions.json
├── netlify.toml
├── nixpacks.toml
├── .lintstagedrc.json
├── tsconfig.node.json
├── index.html
├── knip.ts
├── pwa-assets.config.ts
├── .github
└── workflows
│ ├── update-license.yml
│ └── release.yml
├── .gitignore
├── commitlint.config.ts
├── vitest.config.ts
├── renovate.json
├── turbo.json
├── tsconfig.json
├── LICENSE
├── vite.config.ts
├── README.md
├── package.json
└── biome.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | v24.11.1
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | pnpm lint-staged
2 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | pnpm check:turbo
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | enable-pre-post-scripts=true
2 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | pnpm commitlint --edit $1
2 |
--------------------------------------------------------------------------------
/src/lib/utils/sample.ts:
--------------------------------------------------------------------------------
1 | export const sum = (a: number, b: number) => {
2 | return a + b;
3 | };
4 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://openapi.vercel.sh/vercel.json",
3 | "buildCommand": "turbo run build"
4 | }
5 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # *
2 | User-agent: *
3 | Allow: /
4 |
5 | # Host
6 | Host: https://vite-react-chakra-starter.sznm.dev
7 |
--------------------------------------------------------------------------------
/src/lib/services/constants.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient } from '@tanstack/react-query';
2 |
3 | export const queryClient = new QueryClient();
4 |
--------------------------------------------------------------------------------
/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from '@tanstack/react-router';
2 |
3 | import Home from '@/lib/pages/home';
4 |
5 | export const Route = createFileRoute('/')({
6 | component: Home,
7 | });
8 |
--------------------------------------------------------------------------------
/.vscode/settings.template.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib",
3 | "typescript.preferences.importModuleSpecifier": "non-relative",
4 | "editor.defaultFormatter": "biomejs.biome"
5 | }
6 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[redirects]]
2 | from="/*"
3 | to="/index.html"
4 | status=200
5 |
6 | [build]
7 | command = 'pnpm build'
8 |
9 | [build.environment]
10 | NODE_VERSION="22"
11 | COREPACK_INTEGRITY_KEYS="0"
12 |
--------------------------------------------------------------------------------
/nixpacks.toml:
--------------------------------------------------------------------------------
1 | # https://github.com/railwayapp/nixpacks/issues/1340#issuecomment-3084046921
2 | buildImage = 'ghcr.io/railwayapp/nixpacks:latest'
3 |
4 | [phases.setup]
5 | nixpkgsArchive = '11cb3517b3af6af300dd6c055aeda73c9bf52c48'
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "biomejs.biome",
4 | "dsznajder.es7-react-js-snippets",
5 | "mhutchie.git-graph",
6 | "oderwat.indent-rainbow",
7 | "yoavbls.pretty-ts-errors"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.lintstagedrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
3 | "biome check --write --no-errors-on-unmatched --error-on-warnings"
4 | ],
5 | "*.{ts,js,json,md}": [
6 | "biome check --write --no-errors-on-unmatched --error-on-warnings"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/consistent-type-definitions */
2 | ///
3 |
4 | // biome-ignore lint/complexity/noBannedTypes: -
5 | type ImportMetaEnv = {};
6 |
7 | interface ImportMeta {
8 | readonly env: ImportMetaEnv;
9 | }
10 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vite React Chakra Starter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/knip.ts:
--------------------------------------------------------------------------------
1 | import type { KnipConfig } from 'knip';
2 |
3 | const config: KnipConfig = {
4 | entry: ['src/main.tsx'],
5 | ignore: ['src/**/*.gen.ts'],
6 | project: ['src/**/*.{ts,tsx,js,jsx,css,scss}'],
7 | ignoreBinaries: ['changelogithub'],
8 | };
9 |
10 | export default config;
11 |
--------------------------------------------------------------------------------
/pwa-assets.config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineConfig,
3 | minimal2023Preset as preset,
4 | } from '@vite-pwa/assets-generator/config';
5 |
6 | export default defineConfig({
7 | headLinkOptions: {
8 | preset: '2023',
9 | },
10 | preset,
11 | images: ['public/favicon.svg'],
12 | });
13 |
--------------------------------------------------------------------------------
/.github/workflows/update-license.yml:
--------------------------------------------------------------------------------
1 | name: Update License
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # Update the license once a year on January 1
7 | - cron: "0 0 1 1 *"
8 |
9 | jobs:
10 | update-license:
11 | uses: agustinusnathaniel/workflows/.github/workflows/update-license.yml@main
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | build
5 | dist-ssr
6 | *.local
7 |
8 | yarn-error.log*
9 | .pnpm-debug.log*
10 |
11 | # TS
12 | *.tsbuildinfo
13 |
14 | # Turbo
15 | .turbo
16 |
17 | .vscode/settings.json
18 |
19 | stats.*
20 |
21 | # Test
22 | coverage/
23 |
24 | # ESLint
25 | .eslintcache
26 |
27 | .tanstack
--------------------------------------------------------------------------------
/commitlint.config.ts:
--------------------------------------------------------------------------------
1 | import { RuleConfigSeverity, type UserConfig } from '@commitlint/types';
2 |
3 | const commitlintConfiguration: UserConfig = {
4 | extends: ['@commitlint/config-conventional'],
5 | rules: {
6 | 'scope-case': [RuleConfigSeverity.Error, 'always', 'kebab-case'],
7 | },
8 | };
9 |
10 | export default commitlintConfiguration;
11 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | import tsConfigPaths from 'vite-tsconfig-paths';
3 | import { defineConfig } from 'vitest/config';
4 |
5 | export default defineConfig({
6 | test: {
7 | coverage: {
8 | include: ['src/lib/utils/**/**.{ts,tsx,js,jsx}'],
9 | },
10 | },
11 | plugins: [tsConfigPaths()],
12 | });
13 |
--------------------------------------------------------------------------------
/src/lib/styles/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { createSystem, defaultConfig } from '@chakra-ui/react';
2 |
3 | export const theme = createSystem(defaultConfig, {
4 | theme: {
5 | tokens: {
6 | fonts: {
7 | heading: { value: 'Plus Jakarta Sans Variable, sans-serif' },
8 | body: { value: 'Plus Jakarta Sans Variable, sans-serif' },
9 | },
10 | },
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:base", "group:all"],
4 | "major": {
5 | "enabled": false
6 | },
7 | "updatePinnedDependencies": false,
8 | "stabilityDays": 2,
9 | "timezone": "Asia/Jakarta",
10 | "schedule": ["before 1am on the first day of the month"],
11 | "rangeStrategy": "bump",
12 | "ignoreDeps": ["node", "pnpm"]
13 | }
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { ChakraProvider } from '@chakra-ui/react';
4 |
5 | import { theme } from '@/lib/styles/theme';
6 |
7 | import { ColorModeProvider } from './color-mode';
8 |
9 | export function Provider(props: React.PropsWithChildren) {
10 | return (
11 |
12 | {props.children}
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib/pages/home/index.tsx:
--------------------------------------------------------------------------------
1 | import { Grid } from '@chakra-ui/react';
2 |
3 | import { CTASection } from './components/cta-section';
4 | import { SomeImage } from './components/some-image';
5 | import { SomeText } from './components/some-text';
6 |
7 | const Home = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | );
15 | };
16 |
17 | export default Home;
18 |
--------------------------------------------------------------------------------
/src/lib/pages/home/components/some-text.tsx:
--------------------------------------------------------------------------------
1 | import { Grid, Heading, Text } from '@chakra-ui/react';
2 |
3 | export const SomeText = () => {
4 | return (
5 |
6 |
7 | vite-react-chakra-starter
8 |
9 |
10 | This is a vite react template with Chakra-UI and TypeScript setup.
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 |
16 | - uses: actions/setup-node@v4
17 | with:
18 | node-version: 22.x
19 |
20 | - run: npx changelogithub
21 | env:
22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
23 |
--------------------------------------------------------------------------------
/src/lib/layout/components/header.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex } from '@chakra-ui/react';
2 |
3 | import { ColorModeButton } from '@/lib/components/ui/color-mode';
4 |
5 | export const Header = () => {
6 | return (
7 |
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/src/lib/utils/sample.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, test } from 'vitest';
2 |
3 | import { sum } from './sample';
4 |
5 | describe('sum', () => {
6 | test('positive number additions', () => {
7 | expect(sum(50, 41)).toBe(91);
8 | expect(sum(60, 41)).toBe(101);
9 | });
10 |
11 | test('negative number additions', () => {
12 | expect(sum(-50, -41)).toBe(-91);
13 | expect(sum(-60, -41)).toBe(-101);
14 | });
15 |
16 | test('positive and negative number additions', () => {
17 | expect(sum(-50, 41)).toBe(-9);
18 | expect(sum(-60, 41)).toBe(-19);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/lib/layout/components/footer.tsx:
--------------------------------------------------------------------------------
1 | import { Flex, Link, Text } from '@chakra-ui/react';
2 |
3 | export const Footer = () => {
4 | return (
5 |
12 |
13 | {new Date().getFullYear()} -{' '}
14 |
19 | agustinusnathaniel.com
20 |
21 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "type:check": {
5 | "inputs": ["tsconfig.json", "src/**"],
6 | "outputs": ["*.tsbuildinfo"]
7 | },
8 | "biome:check": {
9 | "outputs": [],
10 | "inputs": ["biome.json", "src/**"]
11 | },
12 | "biome:ci": {
13 | "outputs": [],
14 | "inputs": ["biome.json", "src/**"]
15 | },
16 | "build": {
17 | "dependsOn": ["type:check"],
18 | "outputs": ["build/client/**", "public/**"],
19 | "env": []
20 | },
21 | "test": {
22 | "outputs": ["node_modules/.vite/vitest/**/results.json"],
23 | "inputs": ["vitest.config.ts", "src/**"]
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/layout/index.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex } from '@chakra-ui/react';
2 | import type { ReactNode } from 'react';
3 |
4 | import { Footer } from './components/footer';
5 | import { Header } from './components/header';
6 | import { Meta } from './components/meta';
7 |
8 | type LayoutProps = {
9 | children: ReactNode;
10 | };
11 |
12 | export const Layout = ({ children }: LayoutProps) => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | {children}
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/lib/layout/components/meta.tsx:
--------------------------------------------------------------------------------
1 | const APP_NAME = 'vite-react-chakra-starter';
2 |
3 | export const Meta = () => {
4 | return (
5 | <>
6 | Vite React Chakra Starter
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | >
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/public/assets/chakra-ui-logomark-colored.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/lib/pages/home/components/some-image.tsx:
--------------------------------------------------------------------------------
1 | import { Flex, Image } from '@chakra-ui/react';
2 |
3 | const ICON_SIZE = 22;
4 |
5 | export const SomeImage = () => {
6 | return (
7 |
8 |
14 |
20 |
26 |
32 |
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "strictNullChecks": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "module": "ESNext",
14 | "moduleResolution": "Bundler",
15 | "allowImportingTsExtensions": true,
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "noEmit": true,
19 | "incremental": true,
20 | "jsx": "react-jsx",
21 | "baseUrl": ".",
22 | "paths": {
23 | "@/*": ["src/*"]
24 | },
25 |
26 | /* Linting */
27 | "noUnusedLocals": true,
28 | "noUnusedParameters": true,
29 | "noFallthroughCasesInSwitch": true
30 | },
31 | "include": ["src"],
32 | "exclude": ["node_modules"],
33 | "references": [{ "path": "./tsconfig.node.json" }]
34 | }
35 |
--------------------------------------------------------------------------------
/src/lib/pages/404/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Button,
4 | Grid,
5 | Heading,
6 | Image,
7 | Link,
8 | Text,
9 | } from '@chakra-ui/react';
10 | import { useNavigate } from '@tanstack/react-router';
11 |
12 | const Page404 = () => {
13 | const navigate = useNavigate();
14 |
15 | const handleBackToHome = () => navigate({ to: '/' });
16 |
17 | return (
18 |
19 | Page not Found
20 |
21 |
22 |
23 |
29 | Illustration by Freepik Stories
30 |
31 |
32 |
33 |
34 | It's Okay!
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default Page404;
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Agustinus Nathaniel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps as ChakraButtonProps } from '@chakra-ui/react';
2 | import {
3 | AbsoluteCenter,
4 | Button as ChakraButton,
5 | Span,
6 | Spinner,
7 | } from '@chakra-ui/react';
8 | import { forwardRef } from 'react';
9 |
10 | interface ButtonLoadingProps {
11 | loading?: boolean;
12 | loadingText?: React.ReactNode;
13 | }
14 |
15 | export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
16 |
17 | export const Button = forwardRef(
18 | function Button(props, ref) {
19 | const { loading, disabled, loadingText, children, ...rest } = props;
20 | return (
21 |
22 | {loading && !loadingText ? (
23 | <>
24 |
25 |
26 |
27 | {children}
28 | >
29 | ) : loading && loadingText ? (
30 | <>
31 |
32 | {loadingText}
33 | >
34 | ) : (
35 | children
36 | )}
37 |
38 | );
39 | },
40 | );
41 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/public/assets/vite-logo.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { QueryClientProvider } from '@tanstack/react-query';
2 | import { createRouter, RouterProvider } from '@tanstack/react-router';
3 | import { StrictMode } from 'react';
4 | import ReactDOM from 'react-dom/client';
5 |
6 | import { Provider } from '@/lib/components/ui/provider';
7 | import Page404 from '@/lib/pages/404';
8 | import { queryClient } from '@/lib/services/constants';
9 |
10 | // Import the generated route tree
11 | import { routeTree } from './routeTree.gen';
12 |
13 | // fonts
14 | import '@fontsource-variable/plus-jakarta-sans';
15 |
16 | // Create a new router instance
17 | const router = createRouter({
18 | routeTree,
19 | context: {
20 | queryClient,
21 | },
22 | defaultPreload: 'intent',
23 | scrollRestoration: true,
24 | defaultStructuralSharing: true,
25 | defaultPreloadStaleTime: 0,
26 | defaultPendingComponent: () => (
27 |
30 | ),
31 | defaultNotFoundComponent: () => ,
32 | });
33 |
34 | // Register the router instance for type safety
35 | declare module '@tanstack/react-router' {
36 | interface Register {
37 | router: typeof router;
38 | }
39 | }
40 |
41 | // Render the app
42 | const rootElement = document.getElementById('app');
43 | if (rootElement && !rootElement.innerHTML) {
44 | const root = ReactDOM.createRoot(rootElement);
45 | root.render(
46 |
47 |
48 |
49 |
50 |
51 |
52 | ,
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/public/assets/ts-logo-512.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/routeTree.gen.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | // @ts-nocheck
4 |
5 | // noinspection JSUnusedGlobalSymbols
6 |
7 | // This file was automatically generated by TanStack Router.
8 | // You should NOT make any changes in this file as it will be overwritten.
9 | // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10 |
11 | import { Route as rootRouteImport } from './routes/__root'
12 | import { Route as IndexRouteImport } from './routes/index'
13 |
14 | const IndexRoute = IndexRouteImport.update({
15 | id: '/',
16 | path: '/',
17 | getParentRoute: () => rootRouteImport,
18 | } as any)
19 |
20 | export interface FileRoutesByFullPath {
21 | '/': typeof IndexRoute
22 | }
23 | export interface FileRoutesByTo {
24 | '/': typeof IndexRoute
25 | }
26 | export interface FileRoutesById {
27 | __root__: typeof rootRouteImport
28 | '/': typeof IndexRoute
29 | }
30 | export interface FileRouteTypes {
31 | fileRoutesByFullPath: FileRoutesByFullPath
32 | fullPaths: '/'
33 | fileRoutesByTo: FileRoutesByTo
34 | to: '/'
35 | id: '__root__' | '/'
36 | fileRoutesById: FileRoutesById
37 | }
38 | export interface RootRouteChildren {
39 | IndexRoute: typeof IndexRoute
40 | }
41 |
42 | declare module '@tanstack/react-router' {
43 | interface FileRoutesByPath {
44 | '/': {
45 | id: '/'
46 | path: '/'
47 | fullPath: '/'
48 | preLoaderRoute: typeof IndexRouteImport
49 | parentRoute: typeof rootRouteImport
50 | }
51 | }
52 | }
53 |
54 | const rootRouteChildren: RootRouteChildren = {
55 | IndexRoute: IndexRoute,
56 | }
57 | export const routeTree = rootRouteImport
58 | ._addFileChildren(rootRouteChildren)
59 | ._addFileTypes()
60 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 |
3 | import { devtools } from '@tanstack/devtools-vite';
4 | import { tanstackRouter } from '@tanstack/router-plugin/vite';
5 | import { visualizer } from 'rollup-plugin-visualizer';
6 | import type { PluginOption } from 'vite';
7 | import { defineConfig } from 'vite';
8 | import checker from 'vite-plugin-checker';
9 | import type { VitePWAOptions } from 'vite-plugin-pwa';
10 | import { VitePWA } from 'vite-plugin-pwa';
11 | import tsconfigPaths from 'vite-tsconfig-paths';
12 |
13 | const pwaOptions: Partial = {
14 | // TODO: enable if you want to enable PWA service worker
15 | disable: true,
16 | registerType: 'autoUpdate',
17 | manifest: {
18 | short_name: 'vite-react-chakra-starter',
19 | name: 'Vite React App Template',
20 | lang: 'en',
21 | start_url: '/',
22 | background_color: '#FFFFFF',
23 | theme_color: '#FFFFFF',
24 | dir: 'ltr',
25 | display: 'standalone',
26 | prefer_related_applications: false,
27 | },
28 | pwaAssets: {
29 | disabled: false,
30 | config: true,
31 | },
32 | };
33 |
34 | // https://vitejs.dev/config/
35 | export default defineConfig(({ mode }) => {
36 | const isCheckDisabled = mode === 'production' || !!process.env.VITEST;
37 | return {
38 | plugins: [
39 | devtools(),
40 | tanstackRouter({ autoCodeSplitting: true }),
41 | ...(!isCheckDisabled
42 | ? [
43 | checker({
44 | typescript: true,
45 | }),
46 | ]
47 | : []),
48 | tsconfigPaths(),
49 | visualizer({ template: 'sunburst' }) as unknown as PluginOption,
50 | VitePWA(pwaOptions),
51 | ],
52 | server: {
53 | port: 3000,
54 | open: true,
55 | },
56 | };
57 | });
58 |
--------------------------------------------------------------------------------
/src/lib/pages/home/components/cta-section.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, Image, Link } from '@chakra-ui/react';
2 | import { AiFillGithub } from 'react-icons/ai';
3 |
4 | import { Button } from '@/lib/components/ui/button';
5 |
6 | const repoName = 'vite-react-chakra-starter';
7 | const repoLink = `https://github.com/agustinusnathaniel/${repoName}`;
8 |
9 | export const CTASection = () => (
10 |
11 |
12 |
21 |
26 |
27 |
28 |
34 |
39 |
40 |
46 |
51 |
52 |
53 |
54 | );
55 |
--------------------------------------------------------------------------------
/src/lib/components/ui/color-mode.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import type { IconButtonProps } from '@chakra-ui/react';
4 | import { ClientOnly, IconButton, Skeleton } from '@chakra-ui/react';
5 | import { ThemeProvider, type ThemeProviderProps, useTheme } from 'next-themes';
6 | import { forwardRef } from 'react';
7 | import { LuMoon, LuSun } from 'react-icons/lu';
8 |
9 | export function ColorModeProvider(props: ThemeProviderProps) {
10 | return (
11 |
12 | );
13 | }
14 |
15 | export function useColorMode() {
16 | const { resolvedTheme, setTheme } = useTheme();
17 | const toggleColorMode = () => {
18 | setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
19 | };
20 | return {
21 | colorMode: resolvedTheme,
22 | setColorMode: setTheme,
23 | toggleColorMode,
24 | };
25 | }
26 |
27 | export function useColorModeValue(light: T, dark: T) {
28 | const { colorMode } = useColorMode();
29 | return colorMode === 'light' ? light : dark;
30 | }
31 |
32 | export function ColorModeIcon() {
33 | const { colorMode } = useColorMode();
34 | return colorMode === 'light' ? : ;
35 | }
36 |
37 | interface ColorModeButtonProps extends Omit {}
38 |
39 | export const ColorModeButton = forwardRef<
40 | HTMLButtonElement,
41 | ColorModeButtonProps
42 | >(function ColorModeButton(props, ref) {
43 | const { toggleColorMode } = useColorMode();
44 | return (
45 | }>
46 |
60 |
61 |
62 |
63 | );
64 | });
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | This is a project bootstrapped with [`@vitejs/app`](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) (`react-ts`), added with [Chakra UI](https://chakra-ui.com) and [TypeScript](https://www.typescriptlang.org) setup.
4 |
5 | - ⚡ blazing fast dev server and build
6 | - 🔗 route management added (`react-router` v7 - Framework configuration)
7 |
8 | [**Live Demo**](https://vite-react-chakra-starter.sznm.dev/)
9 |
10 | [](https://vercel.com/import/git?s=https://github.com/agustinusnathaniel/vite-react-chakra-starter) [](https://app.netlify.com/start/deploy?repository=https://github.com/agustinusnathaniel/vite-react-chakra-starter)
11 |
12 | [](https://stackblitz.com/github/agustinusnathaniel/vite-react-chakra-starter)
13 |
14 | ## Getting Started
15 |
16 | You can either click [`Use this template`](https://github.com/agustinusnathaniel/vite-react-chakra-starter/generate) button on this repository and clone the repo or use npx degit like so:
17 |
18 | ```bash
19 | npx degit agustinusnathaniel/vite-react-chakra-starter
20 | ```
21 |
22 | Then, run the development server:
23 |
24 | ```bash
25 | pnpm dev
26 | ```
27 |
28 | ## Deployment
29 |
30 | - build command: `pnpm build`
31 | - output directory: `build/client`
32 |
33 | ### Vercel
34 |
35 | - https://vercel.com/docs/frameworks/react-router#vercel-react-router-preset
36 |
37 | ### Netlify
38 |
39 | - https://docs.netlify.com/frameworks/react-router/
40 |
41 | ## References
42 |
43 | - [vite](https://vitejs.dev)
44 | - [avoid manual import](https://vitejs.dev/guide/features.html#jsx)
45 | - [Chakra UI](https://chakra-ui.com/)
46 | - [TypeScript](https://www.typescriptlang.org)
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-react-chakra-starter",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "agustinusnathaniel",
6 | "type": "module",
7 | "engines": {
8 | "node": "^24.11.x"
9 | },
10 | "packageManager": "pnpm@10.24.0",
11 | "pnpm": {
12 | "neverBuiltDependencies": []
13 | },
14 | "scripts": {
15 | "dev": "vite",
16 | "build": "vite build",
17 | "serve": "vite preview",
18 | "generate-pwa-assets": "pwa-assets-generator",
19 | "biome:check": "biome check --no-errors-on-unmatched --error-on-warnings",
20 | "biome:fix": "pnpm biome:check --write",
21 | "biome:ci": "biome ci",
22 | "turbo": "pnpm dlx turbo@2.6.1",
23 | "type:check": "tsc",
24 | "test": "vitest run",
25 | "test:ui": "vitest --ui --coverage",
26 | "test:coverage": "vitest --coverage run",
27 | "check:turbo": "pnpm turbo biome:check type:check test",
28 | "knip": "knip",
29 | "up-interactive": "pnpm up -i",
30 | "up-latest": "pnpm up-interactive -L",
31 | "release": "cross-env HUSKY=0 commit-and-tag-version",
32 | "push-release": "git push --follow-tags origin main",
33 | "prepare": "husky"
34 | },
35 | "dependencies": {
36 | "@chakra-ui/react": "^3.30.0",
37 | "@emotion/react": "^11.14.0",
38 | "@fontsource-variable/plus-jakarta-sans": "^5.2.8",
39 | "@tanstack/react-devtools": "^0.8.2",
40 | "@tanstack/react-query": "^5.90.11",
41 | "@tanstack/react-query-devtools": "^5.91.1",
42 | "@tanstack/react-router": "^1.139.12",
43 | "@tanstack/react-router-devtools": "^1.139.12",
44 | "next-themes": "^0.4.6",
45 | "react": "^19.2.0",
46 | "react-dom": "^19.2.0",
47 | "react-icons": "^5.5.0"
48 | },
49 | "devDependencies": {
50 | "@biomejs/biome": "2.3.2",
51 | "@chakra-ui/cli": "^3.30.0",
52 | "@commitlint/cli": "^20.1.0",
53 | "@commitlint/config-conventional": "^20.0.0",
54 | "@commitlint/types": "^20.0.0",
55 | "@tanstack/devtools-vite": "^0.3.11",
56 | "@tanstack/router-plugin": "^1.139.12",
57 | "@types/node": "^24.10.1",
58 | "@types/react": "19.2.2",
59 | "@types/react-dom": "^19.2.3",
60 | "@vite-pwa/assets-generator": "^1.0.2",
61 | "@vitest/coverage-v8": "^4.0.14",
62 | "@vitest/ui": "^4.0.14",
63 | "commit-and-tag-version": "^12.6.0",
64 | "cross-env": "^10.1.0",
65 | "husky": "^9.1.7",
66 | "knip": "^5.70.2",
67 | "lint-staged": "^16.2.7",
68 | "rollup-plugin-visualizer": "^6.0.5",
69 | "typescript": "5.9.3",
70 | "vite": "npm:rolldown-vite@7.1.20",
71 | "vite-plugin-checker": "^0.11.0",
72 | "vite-plugin-pwa": "^1.2.0",
73 | "vite-tsconfig-paths": "^5.1.4",
74 | "vitest": "^4.0.14"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
3 | "vcs": {
4 | "enabled": false,
5 | "clientKind": "git",
6 | "useIgnoreFile": false
7 | },
8 | "files": {
9 | "ignoreUnknown": false,
10 | "includes": [
11 | "src/**/*",
12 | "*.config.ts",
13 | "!src/*.gen.ts",
14 | "!turbo",
15 | "!src/lib/components/ui/**/*",
16 | "!*.css",
17 | "!.tanstack",
18 | "!*.d.ts"
19 | ]
20 | },
21 | "formatter": {
22 | "enabled": true,
23 | "indentStyle": "space"
24 | },
25 | "linter": {
26 | "enabled": true,
27 | "rules": {
28 | "recommended": true,
29 | "a11y": {
30 | "useSemanticElements": "error"
31 | },
32 | "complexity": {
33 | "noExcessiveCognitiveComplexity": "error",
34 | "noUselessStringConcat": "error",
35 | "noUselessUndefinedInitialization": "error",
36 | "useSimplifiedLogicExpression": "error",
37 | "noVoid": "warn"
38 | },
39 | "correctness": {
40 | "noUnusedImports": "error",
41 | "noUnusedVariables": "error",
42 | "noUnusedFunctionParameters": "error",
43 | "useHookAtTopLevel": "error"
44 | },
45 | "performance": {
46 | "noBarrelFile": "error",
47 | "useTopLevelRegex": "error"
48 | },
49 | "style": {
50 | "noDefaultExport": "error",
51 | "useBlockStatements": "error",
52 | "useCollapsedElseIf": "error",
53 | "useDefaultSwitchClause": "error",
54 | "useConsistentArrayType": {
55 | "level": "error",
56 | "options": {
57 | "syntax": "generic"
58 | }
59 | },
60 | "useFilenamingConvention": {
61 | "level": "error",
62 | "options": {
63 | "filenameCases": ["kebab-case"]
64 | }
65 | }
66 | },
67 | "suspicious": {
68 | "noDuplicateElseIf": "error",
69 | "noConsole": {
70 | "level": "error",
71 | "options": {
72 | "allow": ["error", "info"]
73 | }
74 | },
75 | "noEmptyBlockStatements": "error",
76 | "useAwait": "error"
77 | }
78 | }
79 | },
80 | "javascript": {
81 | "formatter": {
82 | "quoteStyle": "single"
83 | }
84 | },
85 | "overrides": [
86 | {
87 | "includes": ["src/lib/pages/**/index.tsx", "src/*.ts", "*.ts"],
88 | "linter": {
89 | "rules": {
90 | "style": {
91 | "noDefaultExport": "off"
92 | }
93 | }
94 | }
95 | }
96 | ],
97 | "assist": {
98 | "enabled": true,
99 | "actions": {
100 | "source": {
101 | "organizeImports": {
102 | "level": "on",
103 | "options": {
104 | "groups": [
105 | [":URL:", ":NODE:", ":PACKAGE:"],
106 | ":BLANK_LINE:",
107 | [":ALIAS:"],
108 | ":BLANK_LINE:",
109 | [":PATH:"]
110 | ]
111 | }
112 | }
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/routes/__root.tsx:
--------------------------------------------------------------------------------
1 | import { TanStackDevtools } from '@tanstack/react-devtools';
2 | import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
3 | import { createRootRoute, HeadContent, Outlet } from '@tanstack/react-router';
4 | import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
5 |
6 | import { Layout } from '@/lib/layout';
7 |
8 | const title = 'Vite React Chakra Starter';
9 | const description = 'app starter template';
10 | const url = 'https://vite-react-chakra-starter.sznm.dev';
11 | const ogImgUrl =
12 | 'https://og.sznm.dev/api/generate?heading=vite-react-chakra-starter&text=React+vite+template+with+Chakra+UI+and+TypeScript+setup.&template=color';
13 |
14 | export const Route = createRootRoute({
15 | head: () => ({
16 | meta: [
17 | {
18 | title,
19 | },
20 | {
21 | name: 'description',
22 | content: description,
23 | },
24 | {
25 | name: 'viewport',
26 | content: 'width=device-width, initial-scale=1.0',
27 | },
28 | {
29 | name: 'application-name',
30 | content: title,
31 | },
32 | {
33 | name: 'apple-mobile-web-app-capable',
34 | content: 'yes',
35 | },
36 | {
37 | name: 'apple-mobile-web-app-status-bar-style',
38 | content: 'default',
39 | },
40 | {
41 | name: 'apple-mobile-web-app-title',
42 | content: title,
43 | },
44 | {
45 | name: 'format-detection',
46 | content: 'telephone=no',
47 | },
48 | {
49 | name: 'mobile-web-app-capable',
50 | content: 'yes',
51 | },
52 | {
53 | name: 'theme-color',
54 | content: '#000000',
55 | },
56 | {
57 | name: 'og:type',
58 | content: 'website',
59 | },
60 | {
61 | name: 'og:url',
62 | content: url,
63 | },
64 | {
65 | name: 'og:title',
66 | content: title,
67 | },
68 | {
69 | name: 'og:description',
70 | content: description,
71 | },
72 | {
73 | name: 'og:image',
74 | content: ogImgUrl,
75 | },
76 | {
77 | name: 'twitter:card',
78 | content: 'summary_large_image',
79 | },
80 | {
81 | name: 'twitter:url',
82 | content: url,
83 | },
84 | {
85 | name: 'twitter:title',
86 | content: title,
87 | },
88 | {
89 | name: 'twitter:description',
90 | content: description,
91 | },
92 | {
93 | name: 'twitter:image',
94 | content: ogImgUrl,
95 | },
96 | ],
97 | links: [
98 | {
99 | rel: 'icon',
100 | href: '/favicon.ico',
101 | },
102 | {
103 | rel: 'apple-touch-icon',
104 | href: '/apple-touch-icon-180x180.png',
105 | },
106 | {
107 | rel: 'manifest',
108 | href: '/manifest.webmanifest',
109 | },
110 | ],
111 | }),
112 | component: () => (
113 | <>
114 |
115 |
116 |
117 |
118 | ,
123 | },
124 | {
125 | name: 'TanStack Query',
126 | render: ,
127 | },
128 | ]}
129 | />
130 | >
131 | ),
132 | });
133 |
--------------------------------------------------------------------------------
/public/assets/react-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/public/assets/404 Error-rafiki.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/Building blocks-amico.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------