Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik13 min czytania

tldraw, nieskończone płótno i licencja SDK

tldraw to SDK nieskończonego płótna dla Reacta. Od wersji 4.0 produkcja wymaga klucza licencyjnego. Wdrożenie, własne kształty, koszty i alternatywy.

tldraw, nieskończone płótno i licencja SDK

tldraw to biblioteka Reacta, która daje gotowy edytor nieskończonego płótna: kształty, strzałki dowiązane do obiektów, warstwy, cofanie, eksport i synchronizację między użytkownikami. Osadzasz jeden komponent i masz działającą tablicę.

Zanim jednak wpiszesz ją do projektu, przeczytaj następną sekcję. Warunki użycia zmieniły się w sposób, który zaskoczył sporo osób, a większość materiałów w sieci nadal opisuje stan sprzed tej zmiany.

Licencja, czyli rzecz, o której trzeba wiedzieć najpierw

Do wersji 3 tldraw dawało się używać bez formalności. Od wersji 4.0, wydanej we wrześniu 2025 roku, SDK nie działa na produkcji bez ważnego klucza licencyjnego, a kod źródłowy stoi pod własną licencją twórców, nie pod Apache 2.0 ani MIT. Bieżącą serią jest dziś piątka, wydana w maju 2026 roku, i to na niej stoi dokumentacja oraz przykłady.

Trzy ścieżki wyglądają tak.

Wersja próbna trwa sto dni i jest darmowa, dostajesz ją po wypełnieniu formularza. Klucz próbny nie ma okresu przejściowego, w odróżnieniu od licencji rocznej, której po dacie wygaśnięcia przysługuje jeszcze trzydzieści dni normalnej pracy. Warto też wiedzieć, jak to wygląda w praktyce: edytor startuje normalnie, działa około pięciu sekund, a potem sam znika ze strony i zostawia po sobie pusty element. Dla użytkownika wygląda to jak awaria aplikacji, a nie jak komunikat o licencji.

Licencja hobbystyczna jest dla projektów niekomercyjnych i wymaga pokazania na płótnie znaku wodnego „made with tldraw". Przyznawana uznaniowo, po opisaniu projektu.

Licencja komercyjna wymaga kontaktu z zespołem sprzedaży. Cennik nie jest opublikowany, a kwota podawana publicznie przez społeczność, rzędu sześciu tysięcy dolarów rocznie na zespół, to punkt odniesienia, a nie oferta. Dla startupów bywają osobne warunki, więc jeśli budujesz produkt, po prostu zapytaj.

Klucz sprawdzany jest po stronie przeglądarki, przez weryfikację podpisu, więc sama walidacja nie potrzebuje sieci. Zgłaszanie użycia to jednak osobna rzecz. Przy kluczu próbnym, przy licencji hobbystycznej ze znakiem wodnym oraz przy wdrożeniu produkcyjnym bez ważnego klucza biblioteka odpytuje adres producenta, podając wersję SDK, rodzaj i identyfikator licencji oraz pełny adres strony, na której się uruchomiła. Zawartości płótna ani danych użytkowników nie wysyła nigdy, natomiast adres wdrożenia owszem. Przy zwykłej licencji komercyjnej bez znaku wodnego to zapytanie nie leci wcale. Klucz jest dodatkowo związany z listą domen, więc ten sam ciąg znaków nie zadziała pod innym adresem.

Praktyczny wniosek: tldraw przestało być kandydatem na tablicę w projekcie pobocznym bez budżetu. Do zabawy i do nauki nadal się nadaje, do produktu komercyjnego wymaga decyzji finansowej podjętej świadomie, a nie odkrytej w dniu wdrożenia.

Kiedy to jest właściwy wybór

Skoro jest płatne, pytanie brzmi: za co się płaci.

Płaci się za to, że nieskończone płótno jest znacznie trudniejsze do napisania, niż wygląda. Same strzałki, które trzymają się kształtów przy przesuwaniu, obracaniu i zmianie rozmiaru, to tygodnie pracy. Do tego dochodzi wykrywanie kolizji, przyciąganie do siatki, zaznaczanie wielokrotne, grupowanie, historia zmian odporna na współbieżne edycje i obsługa rysika z naciskiem.

tldraw ma to wszystko dopracowane do poziomu, na którym nie widać szwów. Jeśli budujesz produkt, w którym płótno jest głównym interfejsem, na przykład narzędzie do projektowania procesów, planowania przestrzeni albo edytor diagramów, sto dni okresu próbnego wystarczy, żeby ocenić, czy własna implementacja zajęłaby więcej niż roczna licencja. Zwykle zajęłaby.

Jeśli natomiast potrzebujesz prostego rysowania obok głównej funkcji aplikacji, na przykład adnotacji na obrazku albo małego szkicownika w notatkach, to jest armata na wróbla i lepiej rozejrzeć się za czymś lżejszym.

Pierwsze uruchomienie

Code
Bash
pnpm add tldraw
Code
TypeScript
import { Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'

export function Whiteboard() {
  return (
    <div style={{ position: 'fixed', inset: 0 }}>
      <Tldraw licenseKey={process.env.NEXT_PUBLIC_TLDRAW_LICENSE} />
    </div>
  )
}

Dwie rzeczy sprawiają najwięcej kłopotu przy pierwszym podejściu.

Arkusz stylów trzeba zaimportować ręcznie. Bez niego komponent renderuje się, ale wygląda jak rozsypany zbiór elementów bez pozycjonowania, co łatwo pomylić z błędem konfiguracji budowania.

Kontener musi mieć określoną wysokość. Płótno rozciąga się do rozmiaru rodzica, więc w elemencie o wysokości automatycznej dostaje zero pikseli i znika. Ustaw height w pikselach, procentach albo użyj pozycjonowania stałego.

W Next.js dochodzi trzecia rzecz: biblioteka odwołuje się do obiektów przeglądarki przy montowaniu, więc renderowanie po stronie serwera trzeba wyłączyć.

Code
TypeScript
import dynamic from 'next/dynamic'

const Tldraw = dynamic(
  async () => (await import('tldraw')).Tldraw,
  { ssr: false }
)

Zapisywanie stanu

Płótno domyślnie nie przeżywa odświeżenia strony. Trwałość dopisujesz sam, i to jest pierwsza decyzja architektoniczna do podjęcia.

Code
TypeScript
import { Tldraw, createTLStore, defaultShapeUtils, getSnapshot, loadSnapshot } from 'tldraw'
import { useEffect, useState } from 'react'

const KLUCZ = 'tablica'

export function TrwalaTablica() {
  const [store] = useState(() => {
    const store = createTLStore({ shapeUtils: defaultShapeUtils })
    const zapisane = localStorage.getItem(KLUCZ)
    if (zapisane) loadSnapshot(store, JSON.parse(zapisane))
    return store
  })

  useEffect(() => {
    return store.listen(() => {
      localStorage.setItem(KLUCZ, JSON.stringify(getSnapshot(store)))
    })
  }, [store])

  return <Tldraw store={store} />
}

Zwróć uwagę na kształt wywołań przy migawkach, bo to jedno z najczęstszych miejsc, w których stary przykład z sieci przestaje się kompilować. getSnapshot i loadSnapshot są funkcjami przyjmującymi magazyn jako pierwszy argument, a nie metodami na nim. Zapis w postaci store.getSnapshot() albo store.loadSnapshot(dane) pochodzi ze starszych wersji i na klasie magazynu takich metod już nie ma.

Ten kod działa i jest wystarczający do prototypu, ale ma wadę widoczną dopiero przy większym rysunku. Nasłuch odpala się przy każdej zmianie, w tym przy każdej klatce przeciągania kształtu, więc zapisujesz cały dokument kilkadziesiąt razy na sekundę. Przy dwustu kształtach to zauważalne zacinanie.

Rozwiązanie jest proste: opóźnij zapis o kilkaset milisekund od ostatniej zmiany. Przy zapisie na serwer opóźnienie powinno być większe, rzędu dwóch sekund, i warto wysyłać różnice zamiast pełnego stanu.

Własne kształty

To jest główny powód, dla którego sięga się po tldraw zamiast po gotową tablicę. Możesz dołożyć własny typ obiektu, który zachowuje się jak wbudowane kształty: da się go zaznaczyć, obrócić, dowiązać do niego strzałkę i cofnąć jego utworzenie.

Code
TypeScript
import { BaseBoxShapeUtil, HTMLContainer, T, TLBaseShape } from 'tldraw'

type KsztaltKarty = TLBaseShape<'karta', { w: number; h: number; tytul: string }>

export class KartaUtil extends BaseBoxShapeUtil<KsztaltKarty> {
  static override type = 'karta' as const
  static override props = { w: T.number, h: T.number, tytul: T.string }

  override getDefaultProps(): KsztaltKarty['props'] {
    return { w: 220, h: 120, tytul: 'Nowa karta' }
  }

  override component(shape: KsztaltKarty) {
    return (
      <HTMLContainer style={{ padding: 12, background: '#fff', border: '1px solid #ddd' }}>
        <strong>{shape.props.tytul}</strong>
      </HTMLContainer>
    )
  }

  override getIndicatorPath(shape: KsztaltKarty) {
    const path = new Path2D()
    path.rect(0, 0, shape.props.w, shape.props.h)
    return path
  }
}

Metoda component zwraca zwykły kod Reacta, więc wewnątrz kształtu możesz umieścić dowolny interfejs: pole tekstowe, wykres, obrazek pobrany z serwera. Metoda getIndicatorPath rysuje obrys pokazywany przy zaznaczeniu i zwraca ścieżkę rysowaną na płótnie. Do wersji czwartej włącznie tę samą rolę pełniła metoda indicator, zwracająca element SVG. W piątce obrysy przeniesiono na warstwę płótna, a stara metoda została w kodzie wyłącznie po to, żeby dawne klasy dalej się kompilowały, i nie rysuje już niczego. Przykłady skopiowane z materiałów sprzed tej zmiany dają więc kształt bez widocznego zaznaczenia.

Dwie pułapki. Właściwości kształtu przechodzą przez walidację przy każdej zmianie, więc zmiana schematu w działającej aplikacji wymaga migracji, inaczej stare dokumenty przestają się wczytywać. Oraz: ciężki komponent wewnątrz kształtu renderuje się przy każdym przesunięciu, więc przy setkach obiektów warto opakować go w memoizację.

Współpraca w czasie rzeczywistym

Synchronizacja to osobny pakiet i osobna decyzja. Serwer demonstracyjny nadaje się do prototypu, ale nie do produkcji, bo dane leżą u kogoś innego i mogą zniknąć.

Tu warto sprostować rozpowszechnione nieporozumienie. Nie ma hostowanej usługi synchronizacji sprzedawanej osobno. Dokumentacja mówi wprost, że poza prototypem serwer stawiasz u siebie, a sama biblioteka synchronizacji mieści się w licencji SDK i nie jest dodatkową pozycją na rachunku.

Drogi są więc dwie i obie prowadzą przez Twoją infrastrukturę. Pierwsza to gotowy szablon na Cloudflare, ten sam układ, na którym działa tldraw.com: osobny obiekt trwały na każdy pokój i magazyn obiektowy na obrazy oraz filmy. Druga to wpięcie pakietu z rdzeniem synchronizacji we własne zaplecze w JavaScripcie, obsługujące gniazda sieciowe.

Wybór sprowadza się do tego, ile chcesz utrzymywać. Szablon skraca drogę do działającego pokoju, natomiast uwierzytelnianie, ograniczanie rozmiaru przesyłanych plików, historia dokumentu i wyszukiwanie pokojów pozostają do dopisania po Twojej stronie. Tak czy inaczej trzeba wliczyć w koszt kilka dni pracy plus utrzymanie.

Warto też pomyśleć o uprawnieniach wcześniej niż później. Sama synchronizacja nie wie, kto ma prawo edytować, a kto tylko oglądać, więc kontrola dostępu jest po Twojej stronie. Tryb tylko do odczytu ustawiasz po stronie klienta, ale klient da się oszukać, więc serwer musi odrzucać zmiany od kogoś bez uprawnień.

tldraw kontra alternatywy

RozwiązanieLicencjaKoszt produkcjiKiedy wybrać
tldrawWłasna, klucz wymaganyPłatna licencja rocznaPłótno jest sercem produktu
ExcalidrawMITZeroSzkice odręczne, budżet zerowy
Konva albo FabricMITZeroMasz czas napisać logikę edytora
Gotowa tablica w chmurzeZamkniętaZa użytkownikaNie budujesz produktu, tylko go używasz

Wiersz drugi jest dziś najczęstszą alternatywą i warto go rozważyć uczciwie. Excalidraw ma odręczny styl, którego część osób nie chce, i uboższe możliwości rozszerzania, ale licencja MIT znosi cały problem opisany na początku. Przy prostszych wymaganiach to jest wybór racjonalny.

Wiersz trzeci to biblioteki rysujące na płótnie bez logiki edytora. Dostajesz kształty, transformacje i zdarzenia, natomiast zaznaczanie, cofanie, strzałki i współpracę piszesz sam. To sensowne, gdy potrzebujesz czegoś bardzo nietypowego, a niesensowne, gdy potrzebujesz zwykłej tablicy.

Zostanie na wersji 3 i co to oznacza

Skoro klucz obowiązuje od wersji 4.0, nasuwa się oczywisty pomysł: zostać na trójce i nie płacić. Warto go rozważyć, ale ze świadomością konsekwencji, a pierwszą z nich jest dystans. Trójka wyszła we wrześniu 2024 roku, więc od bieżącej piątki dzielą ją dwie generacje wydań i dwa zestawy zmian łamiących zgodność.

Wersja 3 zachowuje swoje dotychczasowe warunki, więc kod, który już na niej działa, działa dalej. Nie dostaje natomiast poprawek, nowych możliwości ani wsparcia. Przy bibliotece renderującej interfejs w przeglądarce to nie jest neutralne, bo przeglądarki zmieniają się co kilka tygodni, a zachowanie zdarzeń wskaźnika, obsługi rysika i renderowania tekstu bywa źródłem nowych usterek, których nikt już nie naprawi.

Druga konsekwencja dotyczy zależności. Zamrożona wersja ciągnie za sobą zamrożony zakres wersji Reacta i narzędzi budujących. Za dwa lata aktualizacja reszty projektu może wymusić aktualizację tej biblioteki, a wtedy wracasz do decyzji, tylko z większym długiem do spłacenia.

Trzecia to ludzie. Materiały, przykłady i odpowiedzi w sieci opisują coraz częściej wersję nowszą, więc programista dołączający do projektu trafia na dokumentację, która nie pasuje do kodu. To koszt rozłożony w czasie i trudny do zauważenia w zestawieniu wydatków.

Sensowna droga wygląda tak. Jeśli projekt jest zamknięty, skończony i ma określony czas życia, zostanie na starszej wersji jest racjonalne. Jeśli ma się rozwijać przez lata, policz roczną licencję jako koszt utrzymania, a nie jako wydatek jednorazowy, i porównaj go z czasem, który zespół spędziłby na obchodzeniu problemów w zamrożonej bibliotece.

Jest jeszcze wariant trzeci, najrzadziej rozważany: wyjście z tldraw przy okazji przepisywania. Format dokumentu jest tekstowy i czytelny, więc przeniesienie kształtów do innej biblioteki jest wykonalne, choć nie darmowe. Warto to sprawdzić na próbce, zanim decyzja zapadnie w drugą stronę pod presją terminu.

Eksport i wydajność

Eksport do obrazu działa na zaznaczeniu albo na całej stronie i przyjmuje skalę, więc materiał do druku generujesz podnosząc mnożnik.

Code
TypeScript
const { blob, width, height } = await editor.toImage([...editor.getCurrentPageShapeIds()], {
  format: 'png',
  scale: 2,
  background: true,
})

Metoda zwraca obiekt z polami blob, width i height, a nie sam obiekt binarny, więc przypisanie całego wyniku do zmiennej nazwanej blob kończy się plikiem, którego nie da się zapisać. Format wektorowy zachowuje ostrość przy dowolnym powiększeniu i jest lepszym wyborem do dokumentacji. Zapis stanu w formacie tekstowym to nie obraz, tylko pełny dokument z możliwością ponownego wczytania, więc do archiwizacji trzymaj oba.

Przy wydajności trzy rzeczy robią różnicę. Liczba kształtów widocznych jednocześnie, bo poza obszarem widoku i tak nie są rysowane, więc gęste skupisko obiektów w jednym miejscu boli bardziej niż tysiąc rozrzuconych. Złożoność własnych komponentów, o czym wyżej. Oraz obrazy w pełnej rozdzielczości wstawione jako tło, które potrafią zjeść pamięć szybciej niż cała reszta razem wzięta.

Typowe błędy

Pierwszy to założenie, że biblioteka jest darmowa. Materiały sprzed wersji 4.0, a jest ich w sieci większość, mówią o licencji Apache 2.0. To już nieprawda i odkrycie tego po zbudowaniu produktu jest kosztowne.

Drugi to brak wyłączenia renderowania po stronie serwera. W Reakcie uruchamianym na serwerze biblioteka wywala się przy montowaniu na odwołaniu do obiektu przeglądarki.

Trzeci to kontener bez wysokości. Płótno dostaje wtedy zero pikseli i wygląda, jakby komponent się nie zamontował.

Czwarty to zapis stanu przy każdej zmianie bez opóźnienia. Przy przeciąganiu kształtu daje to kilkadziesiąt zapisów na sekundę i widoczne zacinanie.

Piąty to zmiana właściwości własnego kształtu bez migracji. Stare dokumenty przestają się wczytywać, a użytkownik widzi pustą tablicę zamiast swojej pracy.

Szósty to poleganie na serwerze demonstracyjnym w produkcie. Jest przeznaczony do prób i nie daje żadnych gwarancji trwałości danych.

Siódmy to wstawienie klucza licencyjnego do repozytorium publicznego. Klucz jest sprawdzany po stronie przeglądarki, więc i tak trafia do zbudowanego pliku, ale trzymanie go w kodzie źródłowym utrudnia podmianę i miesza środowiska. Wyciągnij go do zmiennej środowiskowej i ustaw osobno dla środowiska testowego i produkcyjnego.

Ósmy to pominięcie testu na urządzeniu dotykowym. Płótno obsługuje dotyk i rysik, ale rozmiary elementów interfejsu dobrane pod wskaźnik myszy bywają na telefonie nietrafialne, a przewijanie strony potrafi konkurować z przesuwaniem widoku. To wychodzi dopiero na prawdziwym sprzęcie, nie w trybie responsywnym przeglądarki.

FAQ

Czy tldraw jest darmowe?

Do rozwoju lokalnego tak, na produkcji nie. Od wersji 4.0 SDK wymaga klucza licencyjnego: darmowego na sto dni w okresie próbnym, hobbystycznego ze znakiem wodnym przy projektach niekomercyjnych albo komercyjnego po kontakcie z zespołem sprzedaży.

Ile kosztuje licencja komercyjna?

Cennik nie jest publiczny, a kwota krążąca w społeczności, około sześciu tysięcy dolarów rocznie na zespół, nie pochodzi z oficjalnego źródła. Dla startupów bywają osobne warunki, więc realną odpowiedź dostaniesz tylko od zespołu sprzedaży.

Czy klucz licencyjny wymaga połączenia z siecią?

Nie, walidacja polega na sprawdzeniu podpisu w przeglądarce i obywa się bez sieci. Osobno działa zgłaszanie użycia: przy kluczu próbnym, przy licencji hobbystycznej ze znakiem wodnym i przy produkcji bez ważnego klucza biblioteka wysyła do producenta wersję SDK, rodzaj i identyfikator licencji oraz adres strony. Zawartości płótna ani danych użytkowników nie wysyła nigdy.

Czym tldraw różni się od Excalidraw?

Licencją i przeznaczeniem. Excalidraw jest na licencji MIT i celuje w szkic odręczny, tldraw jest płatnym SDK do budowania własnych edytorów płótna, z rozbudowanym systemem własnych kształtów i narzędzi. Przy prostej tablicy różnica w możliwościach nie uzasadnia różnicy w koszcie.

Czy mogę dodać własne kształty i narzędzia?

Tak, i to jest główny powód sięgania po tę bibliotekę. Własny kształt to klasa z metodami rysującymi zawartość i obrys, a wewnątrz możesz umieścić dowolny komponent Reacta, łącznie z formularzem albo wykresem.

Dokumentacja i warunki licencji stoją na stronie dla programistów, a kod w repozytorium na GitHubie.