Hi!
Frontend & full-stack developer. I make fast, carefully finished web apps, from first sketch to deploy.
- TypeScript
- React
- NextJS
- NuxtJS
I build appspeople actually enjoy
I am a developer with extensive experience in building web applications.

- Location
- Bydgoszcz, Poland
- Focus
- Full-stack Developer
- Languages
- Polish, English
I work on building applications using NextJS, and in my free time I experiment with less known frameworks and technologies, such as Svelte, Astro or Nuxt.
My ideas for projects are formed based on experience gained from commercial projects and my own experiments and skill development. Unfortunately, I could not showcase commercial projects done for external companies in this portfolio, but in the "Projects" section you will find my own ideas and implementations :)
Courtto Academy
Multi-tenant SaaS for racket-sport schools
The goal was to build a complex SaaS system for managing racket-sport schools - the idea came from my own tennis school, which had no tool like it. The application is largely complete and covers creating facilities, adding members, managing courts and their availability, building lesson groups and handling payments.
The multi-tenant architecture turned out to be the biggest challenge. Keeping each school’s data isolated demanded care not only in the server layer but on the database side as well. Every query is wrapped in a services layer, where scoping by organization id and permission checks happen in one place, and tests cover that the isolation holds.
- +The same court or coach cannot be booked twice for one slot, because an EXCLUDE USING gist constraint in PostgreSQL enforces it, not just a check in the application.
- +Recurring lessons are stored as a local time and a timezone, so “Mondays at 17:00” stays at 17:00 even after the clocks change.
- +Enrolling locks the row in the database, so two people clicking at once cannot take the same last seat. Anyone who does not fit goes on the waitlist.
- +Payments run on two tracks. Schools pay a subscription for Courtto, and parents pay the schools through Stripe Connect, where the school itself is the merchant.
- Role
- Solo - architecture, database, backend and frontend
- Stack
- Nuxt.jsPostgreSQLDrizzle ORMBetter AuthStripe
Screenshots
Raveomat
Events and ticketing platform
Raveomat is a ticketing platform for music events: a public catalogue plus a panel where organisers list their own events. I built it with my own nights in mind, having co-organised a fair few over my years as an artist.
Sign-in taught me the most. At first the server and the browser kept the session separately, and after a token refresh the two could drift apart. In the end both sides use the same Supabase client and the same cookies, and entry to the panel is checked twice, in middleware and in RLS policies on the database side.
- +Public forms go through Astro Actions, so Zod validation, the honeypot and rate limiting all sit together in one place.
- +Rate limits are counted separately for the IP address and the email, and both identifiers are hashed - no raw data lands in the counters.
- +When Redis stops responding, the limiter falls back to in-memory counters instead of disabling the whole form.
- +Posters are cropped on a canvas while still in the browser, and only that finished crop goes to Storage.
- Role
- Solo - frontend, SSR layer and database
- Stack
- AstroSvelte 5SupabaseTailwindZod
Screenshots
Saboteur
Multiplayer card game in the browser
Saboteur is a browser version of the card game of the same name. Three to ten players, joining a room with a six-character code, no accounts. I built it mainly so I could play with friends :)
Hiding information took the most thought. The whole game rests on not knowing who the saboteur is, so hiding a role in the interface is not enough - the client must never receive it at all. The public game state, your hand and your own role are three separate queries - roles are only revealed when the round ends. Nothing sensitive shows up in the network tab.
- +A new card has to line up on every shared edge and connect to the tunnel network, which a breadth-first search from the start card verifies.
- +Empty rooms are deleted by a cron that runs every half minute and drops players who have been silent for forty-five seconds.
- +The host sets the player count, the number of rounds, the turn timer and an optional broken-tool rule, and everyone else sees the changes live.
- Role
- Solo - game logic, realtime backend and frontend
- Stack
- React 19ConvexViteTailwindMotion
Screenshots
PaperUI
A component library of my own, built on bits-ui
PaperUI is a set of Svelte components that came out of other work. I was building a panel for one of my apps and wanted everything to hold to the style I had in mind, so I kept building the pieces myself. Twenty-one of them piled up, so I gathered them in one place and brought them into a common order. Thirteen are below - exactly the same components as in the library, just in this section’s colours.
These components are live. Click them, toggle them, try the keyboard.
View codeDialog · focus trap and scroll lock
Popover · positioning and click-outside dismissal
Tooltip · delay, keyboard support, portal
unnamed · Default · comfortable · one-off