Skip to content

Contributing

Thanks for your interest in Knitly! This guide covers everything you need to know to get started.

  1. Read the README for project overview
  2. Set up development environment
  3. Pick an issue from GitHub Issues
  4. Create a branch and start coding
  • Bun (Node.js runtime)
  • Git
  • ffmpeg (optional, for video processing)
Terminal window
# Clone repository
git clone https://github.com/knitly-app/knitly.git
cd knitly
# Install dependencies
bun install
Terminal window
# Start development servers
bun run dev
# Frontend: http://localhost:5173
# API: http://localhost:3000
Terminal window
# Create admin user
bun --cwd server run seed
# Create admin + test data
bun --cwd server run seed:dev

Override admin credentials:

Terminal window
ADMIN_EMAIL=you@example.com ADMIN_PASSWORD=yourpass bun --cwd server run seed
knitly/
├── frontend/ # Preact SPA
│ ├── src/
│ │ ├── api/ # API client and query keys
│ │ ├── routes/ # Router routes
│ │ ├── components/# UI components
│ │ ├── hooks/ # Custom hooks
│ │ ├── stores/ # State management
│ │ └── test/ # Tests
│ └── dist/ # Built frontend
├── server/ # Hono API server
│ ├── src/
│ │ ├── routes/ # API routes
│ │ ├── middleware/# Request middleware
│ │ ├── lib/ # Database and utilities
│ │ └── __tests__/ # API tests
│ └── uploads/ # File storage
└── custom/ # Gitignored extensions
  • Use hooks, not class components
  • Use functional components
  • Keep components small and focused
  • Use TanStack Query for all server state
  • Use Zustand for client state
  • Derive state as expressions
  • Never duplicate state in useState
  • No useCallback - ever
  • No useEffect unless syncing with external systems
  • Self-documenting names over explanatory comments
  • Strict mode enabled
  • No any types
  • Use branded types for better safety
Terminal window
# Run linting
bun run lint
# Fix linting issues
bun run lint:fix
Terminal window
# For features
git checkout -b feature/your-feature-name
# For bugs
git checkout -b bugfix/your-bug-name
  • Follow existing code patterns
  • Add tests for new functionality
  • Update documentation as needed
Terminal window
# Run all tests
bun test
# Run frontend tests
bun --cwd frontend run test
# Run E2E tests
bun --cwd frontend run test:e2e
# Run specific test
bun test src/test/unit/some-test.test.ts
Terminal window
bun run lint

Use conventional commit format:

feat: add support for video uploads
fix: resolve login issue on mobile
docs: update configuration guide
refactor: move API client to separate module
Terminal window
# Push branch
git push origin feature/your-feature-name
# Create pull request on GitHub
  • Keep PRs focused - one feature or fix per PR
  • Write clear descriptions - explain what changed and why
  • Include screenshots for UI changes
  • Don’t refactor unrelated code in the same PR
  • Respond to review comments promptly
  • Avoid unnecessary complexity
  • Follow existing patterns
  • Assume future maintainers are smart but unfamiliar
  • Optimize for 120 users per instance
  • Database indexes for common queries
  • Lazy loading for routes and components
  • Input validation on all endpoints
  • XSS sanitization on user content
  • Rate limiting on sensitive endpoints
  • SQL parameterized queries
Terminal window
bun --cwd frontend run test

Test files: src/**/*.test.ts and src/**/*.test.tsx

Terminal window
bun --cwd frontend run test

Test files: src/test/component/*.test.tsx

Terminal window
bun --cwd frontend run test:e2e

Test files: src/e2e/*.spec.ts

Terminal window
bun test

Test files: server/src/__tests__/*.test.ts

  1. Update relevant docs in docs/
  2. Update README.md if needed
  3. Add inline comments only for non-obvious logic

For new API endpoints:

  1. Update docs/api.md
  2. Add type definitions
  3. Document request/response format
server/src/routes/greet.js
import { Hono } from "hono";
import { ensureSession } from "../middleware/auth.js";
export const greetRouter = new Hono();
greetRouter.get("/", ensureSession, (c) => {
return c.json({ message: "Hello!" });
});

Mount it in server/src/app.js:

import { greetRouter } from "./routes/greet.js";
app.route("/api/greet", greetRouter);

Components are plain Preact functions. Import hooks from preact/hooks, and only reach for useState when the value can’t be derived.

src/components/Counter.tsx
import { useState } from "preact/hooks";
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}

All server state goes through TanStack Query. A data hook wraps useQuery with a shared query key:

src/hooks/useMembers.ts
import { useQuery } from "@tanstack/react-query";
import { users } from "../api/endpoints";
import { queryKeys } from "../api/queryKeys";
export function useMembers() {
return useQuery({
queryKey: queryKeys.users.all(),
queryFn: () => users.list(),
staleTime: 1000 * 60,
});
}

For writes, wrap useMutation and invalidate the affected query keys in onSuccess. Never mirror server data into useState or Zustand.

**Description**
A clear description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. See error
**Expected behavior**
A clear description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain.
**Environment:**
- Browser: [e.g. Chrome, Safari]
- OS: [e.g. iOS, Windows]
- Knitly Version: [e.g. v0.1.0]
**Problem**
A clear description of what problem this would solve.
**Solution**
A clear description of the proposed solution.
**Alternatives**
Any alternative solutions considered.
**Additional context**
Add any other context or screenshots.
  • Join our Discord (if available)
  • Check existing issues
  • Ask questions in PRs

By contributing, you agree that your contributions will be licensed under the MIT License.


Happy coding! 🎉