A lightweight React framework for building modern web applications.
English | 简体中文
Modern frontend development is overly complex. Painless strips away the complexity while keeping the power.
- Zero complex concepts - No SSR, no server-side runtime, just pure client-side React
- Zero-runtime CSS - Using Linaria, styles are extracted at build time
- Type-safe - Full TypeScript support with zero configuration
- Instant feedback - Hot Module Replacement for instant updates
- React - UI Library
- Native Router - Lightweight routing
- Linaria - Zero-runtime CSS-in-JS
- Vite - Next generation frontend tooling
- TypeScript - Type safety
// routes.ts
export const routes = [
{
path: '/',
component: () => import('./pages/Home')
},
{
path: '/users',
component: () => import('./pages/Users'),
data: fetchUsers // Auto data fetching
}
];// services/user.ts
interface User {
id: number;
name: string;
}
export async function fetchUsers(): Promise<User[]> {
return get('/api/users');
}
// pages/Users.tsx
import { useData } from '@native-router/react';
export default function Users() {
const users = useData<User[]>();
return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
}// services/user.ts (Mock)
interface User {
id: number;
name: string;
}
/**
* @mock {
* "users|5-10": [
* { "id": "@id", "name": "@name" }
* ]
* }
*/
export async function fetchUsers(): Promise<User[]> {
return get('/api/users');
}// components/Button.tsx
import { css } from '@linaria/core';
const styles = css\`
button {
padding: 8px 16px;
border-radius: 4px;
background: #0070f3;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background: #0050a0;
}
\`;
export function Button({ children }) {
return <button class={styles}>{children}</button>;
}# Clone the template
git clone https://github.com/wmzy/painless.git my-app
cd my-app
# Install dependencies
pnpm install
# Start development server
pnpm startpainless/
├── src/
│ ├── components/ # Reusable UI components
│ ├── views/ # Page components
│ ├── services/ # API & data fetching
│ ├── types/ # TypeScript types
│ ├── util/ # Utility functions
│ └── index.tsx # App entry point
├── public/ # Static assets
├── package.json
└── vite.config.ts # Vite configuration
| Command | Description |
|---|---|
pnpm start |
Start development server |
pnpm build |
Build for production |
pnpm preview |
Preview production build |
pnpm lint |
Run ESLint |
<Router
routes={routes}
baseUrl="/app"
onNavigate={(to, from) => console.log(to)}
/>import { Link } from '@native-router/react';
<Link to="/users">Users</Link>
<Link to="/users/1" prefetch>Prefetch on hover</Link>import { useData, useLoading, useError } from '@native-router/react';
function Page() {
const data = useData<MyData>();
const loading = useLoading();
const error = useError();
if (loading) return <Spinner />;
if (error) return <Error error={error} />;
return <div>{data}</div>;
}- Replace
react-scriptswith Vite - Move from
react-routerto@native-router/react - Replace CSS-in-JS with Linaria
- @native-router/react - Routing
- react-toolroom - React utilities
- Linaria - CSS-in-JS
Contributions are welcome! Please read our contributing guide.
MIT