Dioxus — aplikacje wieloplatformowe w Rust z hot-reloadem
Jeden kod źródłowy na web, pulpit, iOS, Androida i serwer, ze składnią bliską JSX i stanem opartym na sygnałach. Najciekawszy jest jednak sposób łączenia klienta z serwerem: jeden atrybut nad funkcją asynchroniczną tworzy trasę na serwerze i typowane wywołanie na kliencie. Omawiamy też, co dokładnie w hot-reloadzie jest gotowe, a co wciąż eksperymentalne, oraz rozmiary pakietów wynikowych.
Nasz stos to Laravel i React, więc pytanie „po co komu framework interfejsu w Rust" jest zasadne i chcę na nie odpowiedzieć uczciwie już na wstępie. Nie po to, żeby zastąpić aplikację internetową klienta. Po to, żeby mieć odpowiedź na trzy sytuacje, w których dziś sięga się po Electron albo po osobny zespół mobilny: narzędzie desktopowe dla klienta, mała aplikacja mobilna do jednego zadania i wewnętrzne narzędzie, które ma po prostu działać na każdym systemie.
Dioxus (github.com/DioxusLabs/dioxus) obiecuje jeden kod źródłowy na web, pulpit, urządzenia mobilne i serwer, ze składnią bliską JSX-owi i stanem opartym na sygnałach. Najciekawsza jest w nim jednak nie wieloplatformowość, a sposób połączenia klienta z serwerem — i od niego zacznę, bo to jedyna rzecz w tym frameworku, która zmieniła moje myślenie o czymś, co robimy codziennie.
Stan projektu
Dane z API GitHuba i crates.io na 8 października 2026:
- 38 995 gwiazdek i 1876 forków, repozytorium założone 15 stycznia 2021,
- podwójna licencja MIT albo Apache-2.0 — standard w świecie Rusta,
- wersja stabilna 0.7.10 z 30 lipca 2026, a w przygotowaniu 0.8.0-alpha.1. Gałąź 0.7 dostała dziesięć wydań poprawkowych od listopada 2025, czyli utrzymanie jest realne, nie deklarowane,
- ostatni push z początku września, 932 tysiące pobrań w ostatnim okresie przy 2,6 miliona w całej historii,
- 771 otwartych zgłoszeń — liczba duża i warta odnotowania,
- README tłumaczone na sześć języków, aktywny Discord, osobna organizacja na GitHubie dla najlepszych bibliotek ekosystemu.
Model utrzymania jest opisany wprost i warto go znać: projekt ma zespół pracujący w pełnym wymiarze, finansowany przez FutureWei, Satellite.im i program akceleracyjny GitHuba, a długoterminowym celem jest samowystarczalność dzięki płatnym narzędziom dla firm. Nie ma tu jeszcze niczego za paywallem, ale kierunek jest zapowiedziany — i to jest uczciwsze niż udawanie, że projekt utrzyma się z dobrej woli.
Trzy linie i sygnały
Podstawowy przykład z README pokazuje wszystko, co trzeba wiedzieć o warstwie widoku:
fn app() -> Element {
let mut count = use_signal(|| 0);
rsx! {
h1 { "High-Five counter: {count}" }
button { onclick: move |_| count += 1, "Up high!" }
button { onclick: move |_| count -= 1, "Down low!" }
}
}Makro rsx! jest odpowiednikiem JSX-a, a stan trzyma się w sygnałach. Autorzy opisują swoją ergonomię jako połączenie najlepszych cech Reacta, Solida i Svelte i — patrząc na ten fragment — nie jest to przesada: zmiana wartości to count += 1 wprost na sygnale, bez funkcji ustawiającej, a interpolacja w tekście działa jak w szablonie.
Dla osoby piszącej w Reakcie różnica jest przy tym istotniejsza, niż wygląda: reaktywność jest drobnoziarnista. Nie ma tu ponownego uruchamiania całego komponentu przy każdej zmianie ani zależności do wypisywania w tablicach — zmiana sygnału odświeża to, co go faktycznie czyta.
Rzecz najciekawsza: serwer i klient w jednym pliku
Ten przykład z repozytorium wart jest przeczytania dwa razy:
fn main() {
dioxus::launch(|| {
let mut message = use_action(get_message);
rsx! {
h1 { "Server says: " }
pre { "{message:?}" }
button { onclick: move |_| message.call("world".into(), 30), "Click me!" }
}
});
}
#[get("/api/:name/?age")]
async fn get_message(name: String, age: i32) -> Result<String> {
Ok(format!("Hello {}, you are {} years old!", name, age))
}To jest kompletna aplikacja pełnostosowa. Atrybut #[get("/api/:name/?age")] nad zwykłą funkcją asynchroniczną robi dwie rzeczy naraz: tworzy trasę na serwerze i generuje typowane wywołanie dostępne na kliencie. Serializacja argumentów i wyniku dzieje się sama, a use_action po stronie widoku daje obiekt ze stanem wywołania.
Warto zobaczyć, co z tego wynika, porównując z tym, jak robimy to dzisiaj. W układzie Laravel plus Inertia ta sama funkcjonalność wymaga: trasy, kontrolera, walidacji żądania, po stronie klienta wywołania z odpowiednim adresem i obsługą stanu, a między nimi — umowy o kształcie danych, której nic nie wymusza. Zmiana typu pola po stronie serwera jest tu wykrywana w najlepszym razie testem, a w najgorszym błędem u użytkownika.
W Dioxusie ta umowa jest sygnaturą funkcji sprawdzaną przez kompilator. Zmiana i32 na String psuje kompilację klienta, nie działanie produkcji. Nie twierdzę, że to powód do przepisywania czegokolwiek — twierdzę, że jest to najlepszy argument, jaki widziałem, za jednym językiem po obu stronach, i wart poznania niezależnie od tego, czy kiedykolwiek napiszemy w tym linijkę.
Pełny stos na axum
Warstwa serwerowa nie jest zabawką — integruje się z axum, jednym z dwóch najpoważniejszych frameworków HTTP w Rust. Zestaw gotowych elementów obejmuje WebSockety, zdarzenia wysyłane przez serwer, strumieniowanie, przesyłanie i pobieranie plików, renderowanie po stronie serwera, formularze, oprogramowanie pośredniczące i generowanie stron statycznych z odświeżaniem przyrostowym.
Katalog przykładów w repozytorium jest tu najlepszą dokumentacją tego, co faktycznie działa: osobne pliki na stan serwera, oprogramowanie pośredniczące, formularze wieloczęściowe, strumieniowe przesyłanie plików, WebSockety, przekierowania, dostęp do nagłówków, parametry zapytania, uwierzytelnianie z formularzem logowania oraz — co ważne dla wdrożeń stopniowych — własną obsługę serwera axum. Innymi słowy: istniejący backend w Rust można podłączyć, zamiast pisać go od nowa.
Hot-reload: co gotowe, a co eksperymentalne
To jest w tytule tematu, więc trzeba rozdzielić dwie rzeczy, bo dokumentacja rozdziela je bardzo starannie:
dx serveodświeża znaczniki, style i zasoby w milisekundach i jest to funkcja gotowa, wbudowana i reklamowana bez zastrzeżeń,dx serve --hotpatchaktualizuje kod Rusta w czasie działania — i jest opisany jako eksperymentalny. To jest zresztą rzecz technicznie imponująca, bo w języku kompilowanym do kodu maszynowego podmiana logiki bez restartu procesu jest trudna.
Praktyczny wniosek: przy pracy nad wyglądem cykl zwrotny jest jak w projekcie frontendowym, a przy pracy nad logiką trzeba się liczyć z rekompilacją — chyba że zaakceptujemy funkcję eksperymentalną. Przy Rust i jego czasach kompilacji ta różnica jest odczuwalna, więc warto to wiedzieć przed wyceną zadania.
Narzędzie dx, czyli codzienna praca
Framework dostarcza własne narzędzie wiersza polecenia i to ono w praktyce decyduje o wrażeniu z pracy. Instalacja idzie skryptem ze strony projektu albo — co w firmowej infrastrukturze wolę — wprost z repozytorium:
cargo install --git https://github.com/DioxusLabs/dioxus dioxus-cli --lockedDalej wszystko sprowadza się do trzech poleceń: dx serve uruchamia aplikację z serwerem rozwojowym i odświeżaniem, dx serve --platform android stawia ją w emulatorze albo na urządzeniu, a dx bundle buduje wersję do wydania. Przełączanie platform jest parametrem, nie osobną konfiguracją projektu — i to jest ta część obietnicy „jeden kod źródłowy", która faktycznie widać w codziennym użyciu.
Warto zauważyć jedną rzecz z instrukcji uruchamiania przykładów: domyślną platformą jest pulpit, a uruchomienie tego samego przykładu w przeglądarce wymaga wyłączenia domyślnych funkcji i włączenia funkcji web. Ten wzorzec — platformy jako flagi funkcji kompilacji — przenika cały framework i jest rzeczą, którą trzeba zrozumieć na początku, bo inaczej pierwsze próby kończą się niejasnymi błędami kompilacji.
Dokumentacja i ekosystem
Dwie rzeczy w podejściu do dokumentacji zasługują na wyróżnienie, bo rozwiązują problemy, które w młodych frameworkach są normą.
Pierwsza: wszystkie elementy HTML i wszystkie nasłuchy zdarzeń są udokumentowane opisami z MDN. Czyli podpowiedź w edytorze przy elemencie rsx! mówi to samo, co dokumentacja przeglądarkowa — nie trzeba pamiętać, czy nazwa atrybutu została w tym frameworku przetłumaczona na coś innego.
Druga: dokumentacja jest budowana w ciągłej integracji razem z samym frameworkiem, żeby nie rozjechać się z kodem, a strona projektu jest jednocześnie poligonem dla nowych funkcji — jest osobnym, publicznym repozytorium napisanym w Dioxusie. Wzorzec „nasza własna dokumentacja jest największą aplikacją na naszym frameworku" jest najlepszym możliwym testem użyteczności.
Ekosystem jest przy tym zorganizowany świadomie: biblioteka standardowa rozszerzeń jest prowadzona przez społeczność, a projekt utrzymuje osobną organizację na GitHubie dla najlepszych bibliotek, które dostają darmowe wsparcie przy aktualizacjach. To jest odpowiedź na typowy problem młodego ekosystemu — biblioteki porzucane przy każdej zmianie wersji frameworka.
Renderery, czyli gdzie to w ogóle działa
Lista jest długa i część pozycji jest zaskakująca:
- web — rysowanie wprost do DOM-u przez WebAssembly, z możliwością wcześniejszego renderowania po stronie serwera i uwodnienia na kliencie,
- pulpit — przez komponent przeglądarki systemowej, z pełnym dostępem do systemu bez komunikacji międzyprocesowej. To jest różnica wobec architektury, w której warstwa interfejsu rozmawia z warstwą systemową przez kanał,
- urządzenia mobilne — iOS i Android, z budowaniem plików
.ipai.apkoraz bezpośrednimi wywołaniami do Javy i Objective-C przy minimalnym narzucie, - renderowanie po stronie serwera z zawieszaniem i uwodnieniem, oraz liveview, czyli interfejs sterowany z serwera,
- eksperymentalny renderer na WGPU (silnik Blitz) oraz Freya oparta na Skii — czyli rysowanie natywne bez komponentu przeglądarki,
- a do tego rzeczy z gatunku „nie spodziewałem się": osadzenie w Bevy, w samym WGPU i uruchamianie na wbudowanym Linuksie.
Rozmiary, na które warto spojrzeć
Największym zarzutem wobec aplikacji desktopowych opartych na technologiach webowych jest waga. Liczby podawane przez projekt są tu konkretne i sprawdzalne: aplikacja internetowa poniżej 50 kilobajtów (README porównuje to z Reactem), aplikacje desktopowe i mobilne poniżej 5 megabajtów, a przenośne pliki wykonywalne poniżej 3 megabajtów.
Polecenie dx bundle buduje i pakuje z pełnymi optymalizacjami, a na potrzeby weba dokłada generowanie obrazów w formacie AVIF, kompresję WebAssembly i minifikację. Dla porównania: aplikacja w Electronie startuje od kilkudziesięciu megabajtów, bo dowozi własną przeglądarkę. Dioxus na pulpicie używa komponentu przeglądarki obecnego w systemie — z czego wynika i mały rozmiar, i pewna zależność od wersji tego komponentu u użytkownika.
Komponenty gotowe do wzięcia
Rzecz, która najbardziej obniża próg wejścia dla osoby przychodzącej z Reacta: projekt dostarcza własny zestaw komponentów podstawowych wzorowanych na shadcn/ui i Radix Primitives. Kto pracował z którymkolwiek z nich, zna ten model — komponenty bez narzuconych stylów, z gotową obsługą dostępności i zachowań, do ostylowania po swojemu.
Stylowanie odbywa się przez HTML i CSS, z wbudowaną obsługą TailwindCSS albo dowolnej innej biblioteki. Czyli cała wiedza frontendowa zespołu przenosi się tu bez straty — nowa jest składnia i język, nie sposób budowania interfejsu.
Pułapki
- Wersje 0.x i zmiany łamiące zgodność między wydaniami pomocniczymi. Ścieżka 0.6 → 0.7 → 0.8 nie jest bezbolesna, a alfa kolejnej wersji już trwa. Przypnij wersję i zaplanuj czas na migracje,
- Dokumentacja miejscami nie dogania kodu. README odsyła po przykłady dla „najnowszego wydania stabilnego" do gałęzi 0.6, choć stabilne jest 0.7.10. Drobiazg, ale mówiący, jak szybko to się zmienia,
- 771 otwartych zgłoszeń. Przy frameworku o takim zasięgu to jest sygnał zarówno aktywności, jak i zaległości,
- Hot-patching kodu Rusta jest eksperymentalny — przy pracy nad logiką licz się z rekompilacją,
- Renderer natywny jest eksperymentalny. Produkcyjna droga na pulpicie prowadzi przez komponent przeglądarki systemowej,
- Czasy kompilacji Rusta są tym, czym są, i przy dużym projekcie odczuwalnie zmieniają rytm pracy,
- Kadry. To jest realny koszt, o którym trzeba powiedzieć klientowi: osoby piszące w Rust są droższe i trudniej dostępne niż osoby piszące w TypeScripcie, a utrzymanie takiego projektu przez kilka lat jest zobowiązaniem,
- Na weba to WebAssembly — z czego wynika ważenie pakietu, zimny start i inne narzędzia diagnostyczne niż te, które zespół zna,
- Obsługa urządzeń mobilnych jest młoda. Działa i jest reklamowana jako najszybsza droga do aplikacji natywnej w Rust, ale nie ma za sobą lat wdrożeń, jak ekosystem Reacta Native czy Fluttera.
Gdzie to ma sens w naszej pracy
- Narzędzie desktopowe dla klienta. Konfigurator, przeglądarka danych, narzędzie dla działu produkcji — coś, co ma działać na Windowsie w hali i nie wymagać przeglądarki. Trzy megabajty zamiast osiemdziesięciu i pełny dostęp do systemu bez komunikacji międzyprocesowej,
- Mała aplikacja mobilna do jednego zadania. Skanowanie kodów, przyjęcie towaru, obsługa zgłoszeń. Jeden kod na oba systemy, bez zespołu mobilnego,
- Narzędzia wewnętrzne dla nas samych. Tu koszt kadrowy jest argumentem odwrotnym — nasza własna nauka nikomu nie jest fakturowana, a narzędzie działa na każdym systemie w zespole,
- Projekty, w których liczy się rozmiar albo brak środowiska uruchomieniowego — wbudowany Linux, urządzenia, kioski,
- Nauka wzorca umowy sprawdzanej przez kompilator. Nawet zostając przy Laravelu i Inertii, warto zobaczyć, jak wygląda granica klient-serwer bez ręcznie utrzymywanej umowy — bo to zmienia pytania, które zadaje się własnej architekturze.
Czego bym nie robił: nie przepisywałbym na to działającej aplikacji internetowej klienta i nie proponowałbym Dioxusa jako domyślnego wyboru na sklep czy panel. Nie z powodów technicznych, a kadrowych i utrzymaniowych — a te przy projekcie prowadzonym latami ważą więcej niż elegancja rozwiązania.
Podsumowanie
- Jeden kod na web, pulpit, iOS, Androida, serwer i kilka miejsc, których się nie spodziewasz,
- Makro
rsx!zamiast JSX-a i sygnały zamiast haków, z reaktywnością drobnoziarnistą i mutacją wprost na sygnale, - Jeden atrybut nad funkcją asynchroniczną tworzy trasę i typowane wywołanie klienta — najciekawsza rzecz w tym frameworku,
- Umowa klient-serwer jest sprawdzana przez kompilator, nie utrzymywana ręcznie,
- Pełny stos na axum, z WebSocketami, strumieniowaniem, formularzami i możliwością podłączenia istniejącego backendu,
dx serveodświeża wygląd w milisekundach; hot-patching kodu Rusta jest eksperymentalny,- Aplikacja internetowa poniżej 50 kB, desktopowa poniżej 5 MB, plik wykonywalny poniżej 3 MB,
- Komponenty podstawowe wzorowane na shadcn/ui i Radix plus stylowanie HTML-em i CSS-em z Tailwindem,
- Platformy są flagami funkcji kompilacji, a nie osobnymi projektami — o czym trzeba wiedzieć przy pierwszym uruchomieniu,
- Elementy HTML udokumentowane opisami z MDN, a dokumentacja budowana w CI razem z frameworkiem,
- Wersje 0.x — przypnij wersję i zaplanuj migracje,
- Zespół w pełnym wymiarze, finansowany zewnętrznie, z zapowiedzią płatnych narzędzi dla firm w przyszłości,
- Największym kosztem nie jest technologia, a kadry — i to trzeba powiedzieć klientowi wprost.
Licencja: Dioxus jest dostępny na MIT albo Apache-2.0, do wyboru — to standardowy układ podwójny przyjęty w ekosystemie Rusta i najbardziej wygodny z możliwych. Obie licencje są permisywne, więc wolno używać komercyjnie, modyfikować, wpinać w produkty zamknięte i redystrybuować; różnica sprowadza się do tego, że Apache-2.0 dokłada jawną licencję patentową od kontrybutorów wraz z klauzulą odwetową, a MIT jest krótszy i prostszy. Praktyczna zaleta wyboru polega na tym, że projekt nie narzuca nam ścieżki: przy audycie licencji u klienta wskazujemy ten wariant, który pasuje do jego polityki, i sprawa jest zamknięta. Nie ma tu open core, edycji korporacyjnej ani funkcji za paywallem — cały framework, narzędzie wiersza polecenia, warstwa serwerowa i komponenty są w tym samym repozytorium na tych samych warunkach. Warto natomiast pamiętać o dwóch rzeczach, które od licencji są niezależne. Po pierwsze, wkład do projektu jest domyślnie objęty tym samym podwójnym licencjonowaniem, o ile kontrybutor nie zastrzeże inaczej — o czym trzeba wiedzieć, wysyłając poprawkę w czasie pracy dla klienta. Po drugie i ważniejsze przy planowaniu budżetu: zespół rdzenia zapowiada dążenie do samowystarczalności przez płatne narzędzia dla firm. Dzisiaj nie ma żadnego elementu odpłatnego i licencja tego nie przewiduje, ale kierunek jest ogłoszony — a to sygnał, żeby przy wieloletnim wdrożeniu obserwować, gdzie w przyszłości przebiegnie granica między częścią darmową a płatną.