Czysty JavaScript, często nazywany vanilla js, nadal jest jednym z najbardziej praktycznych sposobów budowania interakcji w przeglądarce. W tym artykule pokazuję, kiedy takie podejście naprawdę się opłaca, jak wpływa na UX/UI i wydajność oraz gdzie kończy się jego wygoda, a zaczyna sens frameworka.
Najkrótsza droga do decyzji, czy to podejście ma sens
- Bez dodatkowych bibliotek łatwiej utrzymać małe i średnie interakcje, takie jak menu, zakładki, modal czy walidacja formularza.
- DOM jest osobnym interfejsem przeglądarki, więc JavaScript bezpośrednio steruje elementami strony, a nie „samą stroną” w sensie języka.
- Mniej kodu do pobrania zwykle oznacza szybszy start, co ma znaczenie dla UX, zwłaszcza na mobile i słabszych urządzeniach.
- Dobre wyniki daje podejście oparte na prostych modułach, delegacji zdarzeń i progresywnym ulepszaniu interfejsu.
- Framework zaczyna wygrywać tam, gdzie rośnie złożoność stanu, liczba widoków i potrzeba spójnej architektury zespołowej.

Czym jest czysty JavaScript i co naprawdę daje w przeglądarce
W praktyce chodzi o budowanie interfejsu bez Reacta, Vue, Svelte czy jQuery, za to z użyciem natywnych możliwości przeglądarki. To ważne rozróżnienie, bo sam JavaScript to język, a DOM i zdarzenia to już narzędzia przeglądarki, z którymi ten język współpracuje. Gdy wiem, że potrzebuję tylko kilku interakcji, nie dokładam całego ekosystemu do rozwiązania problemu, który da się opisać kilkudziesięcioma linijkami kodu.
Takie podejście nie oznacza regresu technologicznego. Wręcz przeciwnie: często daje większą kontrolę nad strukturą HTML, lepszą czytelność zależności i mniejszą liczbę warstw pośrednich. W UX/UI przekłada się to na szybszy start strony, łatwiejsze utrzymanie prostych interakcji i mniejsze ryzyko, że sama technologia zacznie dominować nad doświadczeniem użytkownika. To właśnie prowadzi do pytania, gdzie ta prostota daje największy efekt.
Dlaczego w UX/UI prostota często wygrywa
W interfejsach użytkownik nie czeka na „architekturę”. Czeka na reakcję: otwarcie menu, rozwinięcie sekcji, potwierdzenie błędu w formularzu, podpowiedź po kliknięciu. Jeśli taka reakcja może działać bez ciężkiego stosu narzędzi, zwykle wygrywa szybciej od złożonego rozwiązania. Każdy dodatkowy skrypt trzeba pobrać, zdekodować, sparsować i wykonać, więc duża ilość kodu JavaScript realnie wpływa na czas potrzebny do interakcji.
W praktyce przekłada się to na kilka korzyści, które użytkownik czuje od razu:
- Lepszy czas do interaktywności - strona szybciej reaguje, bo ma mniej pracy na starcie.
- Stabilniejszy układ - mniej skryptów to mniejsze ryzyko opóźnionych przeskoków i nagłych zmian w UI.
- Łatwiejsza optymalizacja mobile-first - na słabszym sprzęcie różnica między lekkim a rozbudowanym frontendem jest wyraźniejsza.
- Większa przewidywalność - prosta logika interfejsu jest łatwiejsza do testowania i debugowania.
Nie bez znaczenia jest też to, że wiele efektów wizualnych da się dziś zrobić w CSS, a JavaScript zostawić do rzeczywiście dynamicznych zachowań. To dobry punkt wyjścia do decyzji, co warto budować ręcznie, a co lepiej zostawić frameworkom lub po prostu stylom i natywnym API.
Co zbudujesz bez frameworka i kiedy to ma sens
Jeśli projekt dotyczy strony marketingowej, serwisu treści, panelu z kilkoma interakcjami albo aplikacji, w której większość ekranu jest statyczna, czysty kod często wystarcza z dużym zapasem. W takich przypadkach nie potrzebuję rozbudowanego systemu zarządzania stanem, routera ani warstwy abstrakcji nad DOM-em. Potrzebuję po prostu sprawnego mechanizmu, który robi dokładnie to, co użytkownik widzi i klika.
Najczęściej bez frameworka dobrze sprawdzają się:
- menu mobilne i nawigacja typu off-canvas,
- akordeony, zakładki i sekcje typu show/hide,
- modalne okna, toast notifications i lekkie komunikaty systemowe,
- walidacja formularzy po stronie klienta,
- lazy loading obrazów i elementów poniżej linii zgięcia,
- dynamiczne pobieranie danych przez Fetch API,
- prostą personalizację UI, na przykład zapamiętanie trybu widoku lub preferencji użytkownika.
Każdy z tych przypadków ma jedną wspólną cechę: logika jest lokalna, a zakres odpowiedzialności niewielki. To daje komfort pracy i ogranicza ryzyko, że jedna drobna zmiana rozsadzi cały komponent. Gdy jednak interfejs zaczyna mieć wiele współzależnych stanów, temat robi się bardziej wymagający, dlatego warto ustalić zasady pisania kodu od początku.
Jak pisać taki kod, żeby dało się go rozwijać
Największy błąd w podejściu bez frameworka polega nie na tym, że używa się czystego JavaScriptu, tylko na tym, że robi się to bez struktury. Wtedy szybko powstaje plik z przypadkowymi selektorami, ręcznym przepinaniem klas i funkcjami, które wiedzą o sobie za dużo. Ja wolę traktować każdą interakcję jak mały moduł: ma wejście, ma wyjście i nie dotyka niczego poza swoim zakresem.
Moduły i granice odpowiedzialności
Jeśli sekcja odpowiada za rozwijanie treści, niech wie tylko o swoim przycisku, zawartości i klasie stanu. Jeśli formularz ma walidację, niech logika walidacji nie żyje w tym samym miejscu co animacja komunikatu. Taki podział brzmi banalnie, ale robi ogromną różnicę, kiedy po kilku tygodniach wracasz do projektu i nadal chcesz zrozumieć, co się dzieje.
Obsługa zdarzeń bez spaghetti
Zamiast podpinać osobny listener do każdego elementu, często lepiej użyć delegacji zdarzeń. Jeden listener na kontenerze potrafi obsłużyć wiele elementów potomnych, a kod jest krótszy i odporniejszy na zmiany DOM-u. Warto też pamiętać, że `preventDefault()` ma sens tylko wtedy, gdy naprawdę chcesz przejąć kontrolę nad zachowaniem przeglądarki; w wielu przypadkach lepszym rozwiązaniem jest poprawny HTML, a nie blokowanie domyślnej akcji na siłę.
Przeczytaj również: Tree shaking - odchudź JavaScript i przyspiesz UI!
Dostępność nie jako dodatek
W interfejsach opartych o czysty JavaScript łatwo zapomnieć o klawiaturze, fokusie i czytnikach ekranu, bo wszystko wydaje się działać po kliknięciu myszką. To pozorna kompletność. Jeśli budujesz modal, menu lub akordeon, zadbaj o możliwość zamknięcia z klawiatury, widoczny stan fokusu i właściwe atrybuty ARIA tam, gdzie są potrzebne. W praktyce dobry UX to nie tylko „ładnie działa”, ale też „da się z tego korzystać bez frustracji”.
Gdy takie zasady stają się nawykiem, prosty kod przestaje być prosty tylko z nazwy. Wtedy naturalnie pojawia się pytanie, czy framework daje już realną przewagę, czy tylko dodaje warstwę wygody.
Kiedy framework albo biblioteka będą lepszym wyborem
Nie jestem fanem ideologicznego podejścia w żadną stronę. Jeśli projekt ma szybko rosnąć, zawiera dużo ekranów, współdzielony stan, routowanie, złożone formularze i wiele komponentów używanych w różnych miejscach, framework potrafi oszczędzić sporo czasu. Daje spójny model pracy, deklaratywny sposób opisu UI i często lepszą organizację w zespole.
Poniżej proste porównanie, które zwykle pomaga podjąć decyzję:
| Sytuacja | Czysty JavaScript | Framework lub biblioteka |
|---|---|---|
| Landing page, blog, serwis treści | Zwykle wystarcza i daje najlepszy stosunek prostoty do efektu | Często zbędny, jeśli interakcji jest niewiele |
| Formularze, menu, modal, zakładki | Świetny wybór, bo zakres jest mały i lokalny | Może być nadmiarowy, chyba że projekt już i tak jest oparty na frameworku |
| Panel administracyjny z wieloma widokami | Możliwe, ale wymaga większej dyscypliny architektonicznej | Zwykle praktyczniejszy, szczególnie przy rosnącym zespole |
| Dużo współdzielonego stanu i logiki | Szybko robi się trudniej utrzymać | Najczęściej daje czytelniejszy model pracy |
| Potrzeba SSR, ekosystemu i narzędzi zespołowych | Da się, ale nie będzie to najkrótsza droga | Najczęściej rozsądniejszy wybór |
Ta tabela nie ma jednego zwycięzcy, bo nie o wygraną tu chodzi. Chodzi o dopasowanie narzędzia do skali problemu. Jeśli interfejs ma być lekki i szybki, a zakres funkcji jest wąski, ręczne podejście często daje lepszy efekt niż cięższy stack. Jeśli jednak projekt rośnie w stronę aplikacji, rozsądniej jest przejść na narzędzie, które pomoże utrzymać porządek, zanim chaos stanie się kosztowny.
Najczęstsze błędy, które psują efekt bardziej niż sam brak frameworka
W praktyce największe problemy nie wynikają z tego, że ktoś nie użył biblioteki. Wynikają z tego, że próbował zrobić wszystko na skróty. Najczęściej widzę te same potknięcia: za dużo logiki w jednym pliku, brak rozdzielenia danych i widoku, ręczne przepinanie klas bez kontroli stanu oraz ignorowanie zachowań mobilnych i klawiaturowych.
- Używanie `preventDefault()` jako uniwersalnego hamulca zamiast poprawnej semantyki HTML.
- Podpinanie dziesiątek listenerów tam, gdzie wystarczyłaby delegacja zdarzeń.
- Ładowanie całej logiki od razu, nawet jeśli część interakcji pojawia się dopiero po kliknięciu.
- Pomijanie testów na słabszym telefonie, przez co lokalnie „działa świetnie”, a w realnym użyciu już nie.
- Brak porządku w klasach CSS i atrybutach danych, co utrudnia późniejszą konserwację.
Jest jeszcze jedna pułapka, bardziej subtelna: zbyt szybkie dokładanie JavaScriptu do problemów, które można rozwiązać CSS-em albo samym HTML-em. To nie jest oznaka elegancji, tylko często nadmiarowa złożoność. Im mniej kodu trzeba utrzymać, tym większa szansa, że interfejs będzie szybki, stabilny i zrozumiały dla kolejnej osoby w zespole.
Co zostaje z tego podejścia, gdy projekt rośnie
Najbardziej sensowny wniosek jest prosty: czysty JavaScript nie jest konkurencją dla każdego frameworka, tylko narzędziem do świadomego wyboru zakresu. Tam, gdzie interakcja jest niewielka i lokalna, daje lekkość, kontrolę i dobrą wydajność. Tam, gdzie rośnie złożoność aplikacji, warto przejść na rozwiązanie, które lepiej zarządza stanem, komponentami i pracą zespołową.
Ja traktuję to jako praktyczną zasadę projektową: najpierw zbuduj jak najmniej, ale dobrze. Jeśli problem da się rozwiązać natywnie, zrób to natywnie. Jeśli zaczynasz czuć, że kod trzeba ratować architekturą, a nie kolejną poprawką, to zwykle znak, że pora sięgnąć po framework albo bibliotekę. Taki wybór nie jest porażką prostoty, tylko oznaką, że interfejs po prostu urósł do skali, w której potrzebuje innego narzędzia.
