├── .nvmrc
├── public
├── CNAME
└── robots.txt
├── .github
├── FUNDING.yml
├── pull_request_template.md
└── workflows
│ └── deployflow.yml
├── .prettierrc.json
├── src
├── helpers
│ ├── formatNumber.ts
│ └── getUserCount.ts
├── atoms
│ └── userCount.ts
├── index.tsx
├── index.css
├── components
│ └── UserCount.tsx
└── App.tsx
├── .yarnrc.yml
├── .vscode
├── extensions.json
└── settings.json
├── .gitignore
├── index.html
├── vite.config.ts
├── README.md
├── tsconfig.json
├── LICENSE
├── eslint.config.js
├── package.json
└── .yarn
└── plugins
└── yarn-up-all-plugin.cjs
/.nvmrc:
--------------------------------------------------------------------------------
1 | v22.16.0
2 |
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | google.com
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: backmeupplz
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | - Explain what this PR changes
2 | - in one or more
3 | - bullet points
4 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": false,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/src/helpers/formatNumber.ts:
--------------------------------------------------------------------------------
1 | export default function (n: number) {
2 | return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
3 | }
4 |
--------------------------------------------------------------------------------
/src/atoms/userCount.ts:
--------------------------------------------------------------------------------
1 | import { atom } from 'jotai'
2 | import getUserCount from 'helpers/getUserCount'
3 |
4 | export default atom(getUserCount())
5 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import 'index.css'
2 | import { render } from 'preact'
3 | import App from 'App'
4 |
5 | render(, document.getElementById('root') as Element)
6 |
--------------------------------------------------------------------------------
/src/helpers/getUserCount.ts:
--------------------------------------------------------------------------------
1 | export default async function () {
2 | const data = await (await fetch('https://stats.borodutch.com/count')).json()
3 | return data.count as number
4 | }
5 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | plugins:
4 | - path: .yarn/plugins/yarn-up-all-plugin.cjs
5 | spec: "https://github.com/e5mode/yarn-up-all/releases/download/1.1.0/index.js"
6 |
7 | yarnPath: .yarn/releases/yarn-4.9.2.cjs
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "esbenp.prettier-vscode",
5 | "bradlc.vscode-tailwindcss",
6 | "naumovs.color-highlight",
7 | "oderwat.indent-rainbow"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 | @plugin "daisyui" {
3 | themes: caramellatte --default;
4 | }
5 | @plugin "@tailwindcss/typography";
6 |
7 | body {
8 | margin: 0;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/UserCount.tsx:
--------------------------------------------------------------------------------
1 | import { useAtom } from 'jotai'
2 | import formatNumber from 'helpers/formatNumber'
3 | import userCount from 'atoms/userCount'
4 |
5 | export default function () {
6 | const [fetchedUserCount] = useAtom(userCount)
7 | return
User count: {formatNumber(fetchedUserCount)}
8 | }
9 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import UserCount from 'components/UserCount'
2 | import { Suspense } from 'preact/compat'
3 |
4 | export default function () {
5 | return (
6 |
7 |
Frontend template
8 | Loading...}>
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | stats.html
5 |
6 | # local env files
7 | .env
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | # yarn
25 | .pnp.*
26 | .yarn/*
27 | !.yarn/patches
28 | !.yarn/plugins
29 | !.yarn/releases
30 | !.yarn/sdks
31 | !.yarn/versions
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Frontend starter preact
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "editor.formatOnSave": true,
4 | "editor.codeActionsOnSave": {
5 | "source.organizeImports": "explicit",
6 | "source.addMissingImports": "explicit",
7 | "source.removeUnusedImports": "explicit",
8 | "source.fixAll.eslint": "explicit"
9 | },
10 | "eslint.enable": true,
11 | "eslint.validate": [
12 | "javascript",
13 | "javascriptreact",
14 | "typescript",
15 | "typescriptreact"
16 | ],
17 | "editor.quickSuggestions": {
18 | "strings": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import preact from '@preact/preset-vite'
2 | import tailwindcss from '@tailwindcss/vite'
3 | import { visualizer } from 'rollup-plugin-visualizer'
4 | import { defineConfig, Plugin } from 'vite'
5 | import tsconfigPaths from 'vite-tsconfig-paths'
6 |
7 | export default defineConfig({
8 | plugins: [preact(), tsconfigPaths(), tailwindcss()],
9 | build: {
10 | rollupOptions: {
11 | plugins: [
12 | visualizer({
13 | gzipSize: true,
14 | brotliSize: true,
15 | }) as Plugin,
16 | ],
17 | },
18 | outDir: 'dist',
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/.github/workflows/deployflow.yml:
--------------------------------------------------------------------------------
1 | name: Build and deploy app
2 | on:
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | env:
10 | NODE_OPTIONS: '--max_old_space_size=4096'
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 | - name: Install modules
15 | run: yarn
16 | shell: bash
17 | - name: Build code
18 | run: yarn build
19 | shell: bash
20 | - name: Upload artifact
21 | uses: actions/upload-pages-artifact@v3
22 | with:
23 | path: 'dist'
24 | deploy:
25 | needs: build
26 | permissions:
27 | pages: write
28 | id-token: write
29 | environment:
30 | name: github-pages
31 | url: ${{ steps.deployment.outputs.page_url }}
32 | runs-on: ubuntu-latest
33 | steps:
34 | - name: Deploy to GitHub Pages
35 | id: deployment
36 | uses: actions/deploy-pages@v4
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frontend code template in `preact`
2 |
3 | This template is to be used for frontend applications.
4 |
5 | ## Features
6 |
7 | - State management with `jotai`
8 | - Around 13.53kb after brotli compression (this includes **everything**) 😱
9 | - `preact` under the hood with `preact/compat` makes it compatible with virtually any `react` library but still makes it faster
10 | - Full TypeScript support — no dangling types
11 | - `vite` packager and devtools make building and development lightning fast
12 | - `tailwind-css` built-in with 'daisyui'
13 | - GitHub Actions that lint and check the code on pull requests
14 | - `prettier` and `eslint` configured, enabled and formatting your code on save
15 | - List of recommended extensions for VSCode
16 | - It is important to keep the bundle small, so a `stats.html` file is generated on `yarn build` to visually show you the bundle size
17 |
18 | ## Local launch
19 |
20 | 1. Install dependencies with `yarn`
21 | 2. Run the server with `yarn start`
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "jsxImportSource": "preact",
19 | "baseUrl": "src",
20 | "types": ["vite/client"],
21 | "paths": {
22 | "react": ["../node_modules/preact/compat/"],
23 | "react-dom": ["../node_modules/preact/compat/"]
24 | },
25 | "useUnknownInCatchVariables": false,
26 | "noImplicitOverride": true,
27 | "exactOptionalPropertyTypes": true,
28 | "noPropertyAccessFromIndexSignature": true,
29 | "useDefineForClassFields": true
30 | },
31 | "include": ["src"],
32 | "exclude": ["node_modules"]
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present Borodutch Labs
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 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import tseslintParser from '@typescript-eslint/parser'
2 | import prettier from 'eslint-plugin-prettier/recommended'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import tseslint from 'typescript-eslint'
6 |
7 | export default tseslint.config(
8 | {
9 | ignores: ['**/tailwind.config.js', '**/.yarn/**', '**/node_modules/**'],
10 | },
11 | tseslint.configs.recommended,
12 | tseslint.configs.strict,
13 | {
14 | files: ['src/**/*.{js,jsx,ts,tsx}'],
15 | languageOptions: {
16 | parser: tseslintParser,
17 | ecmaVersion: 'latest',
18 | sourceType: 'module',
19 | parserOptions: {
20 | project: './tsconfig.json',
21 | },
22 | globals: {
23 | node: true,
24 | },
25 | },
26 | plugins: {
27 | react,
28 | 'react-hooks': reactHooks,
29 | },
30 | rules: {
31 | '@typescript-eslint/no-floating-promises': 'error',
32 | 'require-await': 'error',
33 | 'react-hooks/exhaustive-deps': 'error',
34 | '@typescript-eslint/explicit-function-return-type': 'off',
35 | '@typescript-eslint/explicit-module-boundary-types': 'off',
36 | },
37 | },
38 | prettier
39 | )
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend-starter",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "type": "module",
6 | "scripts": {
7 | "start": "vite",
8 | "build": "cross-env NODE_ENV=production tsc && vite build",
9 | "pretty": "prettier --check src",
10 | "lint": "yarn pretty && eslint src --max-warnings 0",
11 | "preview": "yarn build && yarn vite preview"
12 | },
13 | "dependencies": {
14 | "@tailwindcss/vite": "^4.1.8",
15 | "jotai": "^2.12.5",
16 | "preact": "^10.26.8"
17 | },
18 | "devDependencies": {
19 | "@preact/preset-vite": "^2.10.1",
20 | "@tailwindcss/typography": "^0.5.16",
21 | "@types/eslint-config-prettier": "^6.11.3",
22 | "@types/node": "^22.15.30",
23 | "@vitejs/plugin-react": "^4.5.1",
24 | "autoprefixer": "^10.4.21",
25 | "cross-env": "^7.0.3",
26 | "daisyui": "^5.0.43",
27 | "eslint": "^9.28.0",
28 | "eslint-config-prettier": "^10.1.5",
29 | "eslint-plugin-prettier": "^5.4.1",
30 | "eslint-plugin-react": "^7.37.5",
31 | "eslint-plugin-react-hooks": "^5.2.0",
32 | "eslint-plugin-tailwindcss": "^3.18.0",
33 | "jiti": "^2.4.2",
34 | "prettier": "^3.5.3",
35 | "prettier-plugin-tailwindcss": "^0.6.12",
36 | "rollup-plugin-visualizer": "^6.0.3",
37 | "tailwindcss": "^4.1.8",
38 | "ts-node": "^10.9.2",
39 | "typescript": "^5.8.3",
40 | "typescript-eslint": "^8.33.1",
41 | "vite": "^6.3.5",
42 | "vite-tsconfig-paths": "^5.1.4"
43 | },
44 | "packageManager": "yarn@4.9.2"
45 | }
46 |
--------------------------------------------------------------------------------
/.yarn/plugins/yarn-up-all-plugin.cjs:
--------------------------------------------------------------------------------
1 | module.exports={name:"yarn-up-all-plugin",factory:a=>{const{Configuration:b,Project:c}=a("@yarnpkg/core"),{Cli:d,Command:e,Option:f}=a("clipanion"),g=a("@yarnpkg/plugin-essentials"),h=a("typanion"),i=(a,b)=>a?`@${a}/${b}`:b,j=(a,b)=>{const c=[...a.values()];return b?c.filter(a=>{const c=i(a[1].scope,a[1].name);return!b.includes(c)}):c};class k extends e{constructor(){super(),this.exclude=f.String("-e,--exclude",{validator:h.isString()})}async execute(){if(!g.default.commands)throw new Error("Yarn commands are not available!");const a=await b.find(this.context.cwd,this.context.plugins),{workspace:e}=await c.find(a,this.context.cwd),f=[...e.manifest.dependencies,...e.manifest.devDependencies],h=j(f,this.exclude?this.exclude.split(" "):null),k=h.map(a=>i(a[1].scope,a[1].name)),l=d.from(g.default.commands);return l.runExit(["up",...k],this.context)}}return k.paths=[["up-all"]],k.usage={category:"Utilities",description:"Yarn 2 plugin that will upgrade all dependencies to their latest version with one simple command",details:"This command will upgrade all dependencies to their latest version. You can exclude certain dependencies from being upgraded by using the `-e,--exclude` option.",examples:[["Upgrade all dependencies","yarn up-all"],["Upgrade all dependencies but exclude a single dependency","yarn up-all --exclude package"],["Upgrade all dependencies but exclude a single dependency","yarn up-all -e package"],["Upgrade all dependencies but exclude multiple dependencies","yarn up-all --exclude \"package1 package2\""]]},{commands:[k]}}};
--------------------------------------------------------------------------------