├── .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]}}}; --------------------------------------------------------------------------------