├── src ├── routes │ ├── about │ │ ├── style.css │ │ └── index.jsx │ ├── profile │ │ ├── style.module.css │ │ └── index.jsx │ └── home │ │ ├── index.jsx │ │ └── style.module.css ├── config.js ├── main.jsx ├── global.d.ts ├── style.css ├── components │ └── header │ │ ├── style.module.css │ │ └── index.jsx └── app.jsx ├── .gitignore ├── assets └── favicon.ico ├── vite.config.js ├── .editorconfig ├── README.md ├── index.html ├── tsconfig.json └── package.json /src/routes/about/style.css: -------------------------------------------------------------------------------- 1 | .about { 2 | padding: 50px; 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developit/preact-vite-template/HEAD/assets/favicon.ico -------------------------------------------------------------------------------- /src/routes/profile/style.module.css: -------------------------------------------------------------------------------- 1 | .profile { 2 | padding: 50px; 3 | } 4 | 5 | .profile h1 { 6 | color: #555; 7 | } 8 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // define your app's configuration here. 3 | // you can use VITE_-prefixed environment variables. 4 | }; 5 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import preact from '@preact/preset-vite'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [preact()] 7 | }); 8 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { hydrate, LocationProvider } from 'preact-iso'; 2 | import { App } from './app'; 3 | 4 | hydrate( 5 | 6 | 7 | , 8 | document.body 9 | ); 10 | -------------------------------------------------------------------------------- /src/routes/home/index.jsx: -------------------------------------------------------------------------------- 1 | import style from './style.module.css'; 2 | 3 | export const Home = () => ( 4 |
5 |

Home

6 |

This component uses standard CSS instead of CSS modules.

7 |
8 | ); 9 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | // CSS Modules 2 | type Mapping = Record; 3 | declare module '*.module.css' { 4 | const mapping: Mapping; 5 | export default mapping; 6 | } 7 | 8 | // import.meta.env 9 | declare interface ImportMeta { 10 | env: Record; 11 | } 12 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | /** These are your app's global styles */ 2 | 3 | html, 4 | body { 5 | margin: 0; 6 | background: #f4f4f4; 7 | font: 100%/1.21 system-ui, helvetica, sans-serif; 8 | } 9 | 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | a { 15 | font: inherit; 16 | color: inherit; 17 | text-decoration: none; 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{.*rc,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [package.json] 15 | insert_final_newline = false 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Preact Vite Template 2 | 3 | This template will have you up-and-running with [Preact] and [Vite] in no time. 4 | 5 | ### Commands 6 | 7 | - `npm start`: start the development server 8 | - `npm run build`: build for production 9 | - `npm run preview`: serve a production build locally for testing 10 | 11 | [preact]: https://preactjs.com 12 | [vite]: https://vite.dev 13 | -------------------------------------------------------------------------------- /src/routes/home/style.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is "Modular CSS", because the filename ends with `.module.css`. 3 | * In CSS Modules, classes are scoped to your component by default. 4 | * To use a class defined in your CSS, you must import its name: 5 | * import style from './style.module.css'; 6 | *
7 | */ 8 | 9 | .home { 10 | padding: 50px; 11 | } 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Preact Vite Template 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/header/style.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | align-items: stretch; 4 | justify-content: flex-start; 5 | height: 3rem; 6 | background: #444; 7 | color: #fff; 8 | } 9 | 10 | .header a { 11 | padding: 0.7rem; 12 | font-size: 120%; 13 | text-decoration: none; 14 | } 15 | .header a:hover, 16 | .header a:focus { 17 | background: #666; 18 | } 19 | .header a[current] { 20 | background: #fff; 21 | color: #444; 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "jsxFactory": "preact.h", 6 | "jsxFragmentFactory": "preact.Fragment", 7 | "allowJs": true, 8 | "checkJs": true, 9 | "noEmit": true, 10 | "moduleResolution": "node", 11 | "target": "ES2017", 12 | "module": "esnext", 13 | "resolveJsonModule": true, 14 | "allowSyntheticDefaultImports": true, 15 | "downlevelIteration": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/routes/profile/index.jsx: -------------------------------------------------------------------------------- 1 | import { useRoute } from 'preact-iso'; 2 | import styles from './style.module.css'; 3 | 4 | export default function Profile({ user = 'me' }) { 5 | const route = useRoute(); 6 | console.log(route.params, route.query); 7 | 8 | const name = user[0].toUpperCase() + user.slice(1); 9 | 10 | return ( 11 |
12 |

{user == 'me' ? 'My' : `${name}'s`} Profile

13 |

This is a page all about {name}.

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/components/header/index.jsx: -------------------------------------------------------------------------------- 1 | import style from './style.module.css'; 2 | import { useLocation } from 'preact-iso'; 3 | 4 | export function Header() { 5 | return ( 6 |
7 | Home 8 | About 9 | My Profile 10 | Alice's Profile 11 |
12 | ); 13 | } 14 | 15 | function Link(props) { 16 | const { path } = useLocation(); 17 | props.current = (props.path || props.href) === path; 18 | return ; 19 | } 20 | -------------------------------------------------------------------------------- /src/routes/about/index.jsx: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | import { useState, useEffect } from 'preact/hooks'; 3 | 4 | export default function About() { 5 | const [time, setTime] = useState(new Date()); 6 | 7 | // after the first render, start a timer to update the current time: 8 | useEffect(() => { 9 | let timer = setInterval(() => { 10 | setTime(new Date()); 11 | }, 1000); 12 | 13 | // stop the timer when the component is unmounted: 14 | return () => clearTimeout(timer); 15 | }, []); 16 | 17 | return ( 18 |
19 |

About

20 | 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/app.jsx: -------------------------------------------------------------------------------- 1 | import { lazy, Router } from 'preact-iso'; 2 | import { Header } from './components/header'; 3 | // Statically-imported routes are loaded with your main bundle: 4 | import { Home } from './routes/home'; 5 | // ... whereas dynamically imported routes are loaded on-demand: 6 | const About = lazy(() => import('./routes/about')); 7 | const Profile = lazy(() => import('./routes/profile')); 8 | 9 | export function App() { 10 | return ( 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-vite-template", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "start": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "preact": "^10.6.6", 11 | "preact-iso": "^2.3.0" 12 | }, 13 | "devDependencies": { 14 | "@preact/preset-vite": "^2.1.7", 15 | "eslint": "^7.32.0", 16 | "eslint-config-developit": "^1.2.0", 17 | "eslint-config-prettier": "^8.5.0", 18 | "postcss-nesting": "^10.1.3", 19 | "prettier": "^2.5.1", 20 | "vite": "^2.8.6" 21 | }, 22 | "postcss": { 23 | "plugins": { 24 | "postcss-nesting": {} 25 | } 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "developit", 30 | "prettier", 31 | "plugin:prettier/recommended" 32 | ], 33 | "plugins": [ 34 | "prettier", 35 | "import" 36 | ] 37 | }, 38 | "prettier": { 39 | "singleQuote": true, 40 | "trailingComma": "none", 41 | "useTabs": true, 42 | "printWidth": 120, 43 | "arrowParens": "avoid" 44 | } 45 | } 46 | --------------------------------------------------------------------------------