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

Flowbite, komponenty UI na Tailwind CSS

Flowbite daje komponenty i bloki UI na Tailwind CSS dla wielu frameworków. Wersja 4, Tailwind v4, warstwa darmowa i płatna oraz dostępność.

Flowbite, komponenty UI na Tailwind CSS

Flowbite to zestaw komponentów interfejsu zbudowanych na Tailwind CSS, dostępny w kilku wariantach: jako gotowy kod HTML do skopiowania, jako pakiet z warstwą JavaScript oraz jako biblioteki dla poszczególnych frameworków. Rdzeń jest na licencji MIT, w wersji 4.0.2, a osobno sprzedawana jest warstwa gotowych bloków i szablonów.

Dwa różne sposoby użycia, które łatwo pomylić

To rozróżnienie decyduje o tym, czego się spodziewać, a bywa zacierane w materiałach o tej bibliotece.

Pierwszy wariant to czysty Tailwind. Kopiujesz kod znaczników z klasami i masz gotowy element: kartę, formularz, tabelę. Nie instalujesz nic, nie ma zależności, a kod jest Twój od momentu wklejenia. Działa to dla wszystkiego, co nie wymaga interakcji.

Drugi wariant potrzebny jest wszędzie tam, gdzie coś ma się otwierać, zamykać albo reagować na klawiaturę. Lista rozwijana, okno modalne, wybór daty, karuzela. Tu potrzebna jest warstwa JavaScript, którą albo dokładasz jako pakiet obsługujący znaczniki przez atrybuty, albo bierzesz w postaci komponentów dla swojego frameworka.

Praktyczna konsekwencja bywa zaskakująca przy pierwszym podejściu. Skopiowanie kodu okna modalnego ze strony projektu i wklejenie go do aplikacji bez zainstalowania warstwy JavaScript daje element, który wygląda poprawnie i nie robi nic. To nie jest błąd, tylko wynik wybrania pierwszego wariantu tam, gdzie potrzebny był drugi.

Warto też wiedzieć, że wariant dla Reacta rządzi się nieco innymi prawami niż reszta. Ma własny numer wersji, własne tempo wydań i własne rozwiązania, w tym mechanizm generujący potrzebne klasy automatycznie. Numer wersji pakietu rdzeniowego nie mówi więc nic o tym, w jakiej wersji jest biblioteka dla Reacta.

Tailwind w wersji 4 i co to zmienia

To najczęstsze źródło problemów przy wdrażaniu, bo dotyczy konfiguracji, a nie samych komponentów.

Tailwind w wersji czwartej przeniósł konfigurację z pliku JavaScript do arkusza stylów. Zamiast obiektu z ustawieniami piszesz teraz dyrektywy w pliku CSS: importujesz moduły, deklarujesz zmienne motywu, wskazujesz pliki do przeskanowania. Wszystkie poradniki opisujące dodanie wtyczki do pliku konfiguracyjnego dotyczą wersji trzeciej i w nowym projekcie nie zadziałają.

Flowbite obsługuje obie wersje, co jest wygodne, ale wymaga świadomego wyboru ścieżki. Warto sprawdzić, którą wersję Tailwinda masz w projekcie, zanim zaczniesz kopiować instrukcje z sieci, bo objawem pomyłki jest brak działania stylów bez żadnego komunikatu błędu.

Osobna sprawa dotyczy skanowania klas. Tailwind generuje wyłącznie te klasy, które znajdzie w plikach źródłowych, więc klasy pochodzące z pakietu w katalogu zależności muszą być objęte skanowaniem. Pominięcie tego kroku daje komponenty bez stylów, co przy dużej liczbie elementów wygląda na uszkodzoną instalację, a jest jedną brakującą linią w konfiguracji. Więcej o samym mechanizmie znajdziesz przy okazji Tailwind CSS.

Ten sam mechanizm tłumaczy inną częstą pomyłkę, o której warto wiedzieć niezależnie od tej biblioteki. Klasy sklejane w czasie działania programu, na przykład przez łączenie fragmentów nazwy ze zmienną, nigdy nie zostaną wygenerowane, bo skaner czyta pliki jako tekst i nie wykonuje kodu. Objawem jest kolor albo odstęp działający dla jednej wartości i milczący dla innej. Rozwiązaniem jest wypisanie pełnych nazw klas w kodzie i wybieranie między nimi, zamiast budowania nazwy z kawałków.

Tryb ciemny i motywy

Obsługa trybu ciemnego jest tu wbudowana w komponenty, ale sposób jej włączania wymaga jednej decyzji, którą warto podjąć świadomie na początku projektu.

Przełączanie może działać na dwa sposoby: podążać za ustawieniem systemu operacyjnego albo być sterowane klasą na elemencie nadrzędnym. Pierwszy wariant jest prostszy i nie wymaga kodu, drugi pozwala dać użytkownikowi przełącznik. W praktyce większość projektów chce obu naraz: domyślnie ustawienie systemu, z możliwością ręcznego nadpisania i zapamiętania wyboru.

Pułapka techniczna dotyczy pierwszego renderowania. Jeśli motyw odczytujesz z pamięci przeglądarki po załadowaniu strony, użytkownik zobaczy przez moment wariant domyślny, a potem ten właściwy. Objawia się to mignięciem jasnego tła u kogoś, kto wybrał ciemny motyw. Standardowym rozwiązaniem jest ustawienie klasy skryptem wykonywanym przed narysowaniem strony, jeszcze przed wczytaniem reszty aplikacji.

Kiedy Flowbite jest dobrym wyborem

Warto nazwać scenariusze wprost, bo ta biblioteka celuje w konkretną niszę i poza nią wypada gorzej od konkurencji.

Najlepiej sprawdza się tam, gdzie projekt nie stoi na jednym frameworku. Agencja budująca strony w różnych technologiach, zespół utrzymujący starszą aplikację obok nowej, projekt, w którym część stron to zwykły HTML generowany po stronie serwera. Ten sam wygląd i ten sam sposób pracy w każdym z tych miejsc to realna oszczędność, której nie da inna biblioteka związana z jednym ekosystemem.

Drugi dobry scenariusz to panel administracyjny albo narzędzie wewnętrzne. Liczy się szybkie złożenie działającego interfejsu z tabel, formularzy i wykresów, a nie oryginalny wygląd. Tu zestaw gotowych komponentów o spójnej estetyce daje przewagę, bo nikt nie musi podejmować decyzji projektowych.

Gorzej wypada przy produkcie, który ma się wyróżniać wizualnie. Komponenty są rozpoznawalne, a strona z nich złożona wygląda jak wiele innych stron. Da się to przemalować, ale wtedy pracy jest tyle, że przewaga startowa znika.

Słabszym wyborem jest też przy aplikacji o wysokich wymaganiach dostępności, o czym niżej, oraz przy zespole, który ma już własny system projektowy. W tym drugim przypadku narzucona struktura klas i konwencje zaczynają przeszkadzać zamiast pomagać.

Czym jest Flowbite?

Flowbite to open-source biblioteka komponentów zbudowana na Tailwind CSS, oferująca ponad 50 gotowych do użycia komponentów UI oraz 450+ bloków (gotowych sekcji stron). W przeciwieństwie do wielu bibliotek, Flowbite jest dostępny dla praktycznie każdego frameworka JavaScript - React, Vue, Svelte, Angular, a także jako vanilla JavaScript i czysty HTML.

Flowbite powstał z myślą o przyspieszeniu developmentu - zamiast budować każdy komponent od zera, możesz skopiować gotowy kod lub zainstalować paczkę i mieć działający navbar, modal czy datepicker w minuty. Wszystkie komponenty są w pełni responsive, wspierają dark mode i są zoptymalizowane pod dostępność.

Filozofia Flowbite

Flowbite oferuje dwa podejścia:

  1. Copy-paste HTML/CSS - Każdy komponent ma kod do skopiowania, działający z czystym Tailwind CSS
  2. Paczki JavaScript - Dla interaktywnych komponentów (dropdown, modal, datepicker) potrzebujesz JS

To sprawia, że Flowbite jest niezwykle elastyczny - możesz użyć tylko kilku komponentów lub zbudować całą aplikację na tej bibliotece.

Flowbite vs Inne Biblioteki

CechaFlowbiteDaisyUITailwind UIshadcn/ui
CenaDarmowy + ProDarmowy$299+Darmowy
FrameworkiWszystkieTailwind onlyReactReact
Bloki sekcji450+Brak500+~50
FigmaTak (Pro)TakNieNie
Dark modeWbudowanyWbudowanyWbudowanyManual
LicencjaMITMITCommercialMIT
JavaScriptOpcjonalnyCSS onlyReactReact
TypeScriptPełneN/APełnePełne

Kiedy wybrać Flowbite?

Wybierz Flowbite, gdy:

  • Potrzebujesz wsparcia dla wielu frameworków
  • Chcesz gotowe bloki (hero, pricing, features)
  • Szukasz darmowej alternatywy dla Tailwind UI
  • Budujesz dashboard lub admin panel
  • Potrzebujesz datepickera, carousel, wyedge drawer

Rozważ alternatywy, gdy:

  • Potrzebujesz tylko CSS bez JS (DaisyUI)
  • Chcesz pełnej kontroli nad kodem (shadcn/ui)
  • Budget pozwala na płatne rozwiązanie (Tailwind UI)

Instalacja

Vanilla JavaScript / HTML

Zanim przejdziesz do poleceń, jedna uwaga o formie konfiguracji. Bloki poniżej pokazują układ z pliku JavaScript, obowiązujący w Tailwindzie w wersji trzeciej. W wersji czwartej, opisanej w poprzedniej sekcji, to samo zapisuje się dyrektywami w arkuszu stylów.

Code
CSS
@import "flowbite/src/themes/default";
@plugin "flowbite/plugin";
@source "../node_modules/flowbite";

Trzy wiersze odpowiadają trzem rzeczom, które w starszym układzie robił plik konfiguracyjny: wciągnięciu motywu, podłączeniu wtyczki i wskazaniu katalogu do przeskanowania w poszukiwaniu klas. Motywów jest kilka i wybierasz jeden, a nazwa w pierwszym wierszu decyduje, który.

Code
Bash
# npm
npm install flowbite

# yarn
yarn add flowbite

# pnpm
pnpm add flowbite
JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  content: [
    "./node_modules/flowbite/**/*.js"
  ],
  plugins: [
    require('flowbite/plugin')
  ]
}
Code
HTML
<!-- Dodaj skrypt przed </body> -->
<script src="../path/to/flowbite/dist/flowbite.min.js"></script>

<!-- Lub z CDN -->
<script src="https://cdn.jsdelivr.net/npm/flowbite@4.0.2/dist/flowbite.min.js"></script>

React

Code
Bash
npm install flowbite flowbite-react
JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  content: [
    './node_modules/flowbite-react/lib/**/*.js',
  ],
  plugins: [
    require('flowbite/plugin')
  ],
}

Vue 3

Code
Bash
npm install flowbite flowbite-vue
JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  content: [
    './node_modules/flowbite-vue/**/*.{js,jsx,ts,tsx}',
    './node_modules/flowbite/**/*.js'
  ],
  plugins: [
    require('flowbite/plugin')
  ],
}

Svelte

Code
Bash
npm install flowbite flowbite-svelte
JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  content: [
    './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'
  ],
  plugins: [
    require('flowbite/plugin')
  ],
}

Angular

Code
Bash
npm install flowbite
JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  content: [
    "./node_modules/flowbite/**/*.js"
  ],
  plugins: [
    require('flowbite/plugin')
  ]
}
angular.json
JSON
// angular.json - dodaj skrypt
"scripts": [
  "node_modules/flowbite/dist/flowbite.min.js"
]

Komponenty React

Alert

Code
TypeScript
import { Alert } from 'flowbite-react'
import { HiInformationCircle, HiX } from 'react-icons/hi'

function AlertExamples() {
  return (
    <div className="flex flex-col gap-4">
      {/* Podstawowy */}
      <Alert color="info">
        <span className="font-medium">Info alert!</span> Change a few things up and try submitting again.
      </Alert>

      {/* Różne kolory */}
      <Alert color="failure">
        <span className="font-medium">Danger alert!</span> Something went wrong.
      </Alert>

      <Alert color="success">
        <span className="font-medium">Success alert!</span> Your purchase is complete.
      </Alert>

      <Alert color="warning">
        <span className="font-medium">Warning alert!</span> Please check your input.
      </Alert>

      {/* Z ikoną */}
      <Alert color="info" icon={HiInformationCircle}>
        <span className="font-medium">Info!</span> This is an informational alert.
      </Alert>

      {/* Z dismiss button */}
      <Alert
        color="success"
        onDismiss={() => console.log('Alert dismissed!')}
      >
        <span className="font-medium">Success!</span> You can dismiss this alert.
      </Alert>

      {/* Z border */}
      <Alert color="info" withBorderAccent>
        <span className="font-medium">Info!</span> Alert with border accent.
      </Alert>

      {/* Z dodatkową treścią */}
      <Alert
        color="info"
        additionalContent={
          <div className="mt-2 mb-4 text-sm text-cyan-700 dark:text-cyan-800">
            More info about this alert goes here. This example text is going to run a bit longer
            so that you can see how spacing works.
          </div>
        }
        icon={HiInformationCircle}
      >
        <span className="font-medium">More information</span>
      </Alert>

      {/* Rounded */}
      <Alert color="info" rounded>
        Rounded alert!
      </Alert>
    </div>
  )
}

Navbar

Code
TypeScript
import { Avatar, Button, Dropdown, DropdownDivider, DropdownHeader, DropdownItem, Navbar, NavbarBrand, NavbarCollapse, NavbarLink, NavbarToggle } from 'flowbite-react'

function NavbarExample() {
  return (
    <Navbar fluid rounded>
      <NavbarBrand href="https://flowbite.com/">
        <img
          src="/logo.svg"
          className="mr-3 h-6 sm:h-9"
          alt="Flowbite Logo"
        />
        <span className="self-center whitespace-nowrap text-xl font-semibold dark:text-white">
          Flowbite
        </span>
      </NavbarBrand>

      <div className="flex md:order-2">
        <Button>Get started</Button>
        <NavbarToggle />
      </div>

      <NavbarCollapse>
        <NavbarLink href="/" active>
          Home
        </NavbarLink>
        <NavbarLink href="/about">About</NavbarLink>
        <NavbarLink href="/services">Services</NavbarLink>
        <NavbarLink href="/pricing">Pricing</NavbarLink>
        <NavbarLink href="/contact">Contact</NavbarLink>
      </NavbarCollapse>
    </Navbar>
  )
}

// Z dropdown

function NavbarWithDropdown() {
  return (
    <Navbar fluid rounded>
      <NavbarBrand href="/">
        <span className="self-center whitespace-nowrap text-xl font-semibold dark:text-white">
          Brand
        </span>
      </NavbarBrand>

      <div className="flex md:order-2">
        <Dropdown
          arrowIcon={false}
          inline
          label={
            <Avatar
              alt="User settings"
              img="/avatar.png"
              rounded
            />
          }
        >
          <DropdownHeader>
            <span className="block text-sm">John Doe</span>
            <span className="block truncate text-sm font-medium">
              john@example.com
            </span>
          </DropdownHeader>
          <DropdownItem>Dashboard</DropdownItem>
          <DropdownItem>Settings</DropdownItem>
          <DropdownDivider />
          <DropdownItem>Sign out</DropdownItem>
        </Dropdown>
        <NavbarToggle />
      </div>

      <NavbarCollapse>
        <NavbarLink href="/" active>Home</NavbarLink>
        <NavbarLink href="/about">About</NavbarLink>
        <NavbarLink href="/services">Services</NavbarLink>
      </NavbarCollapse>
    </Navbar>
  )
}

Modal

Code
TypeScript
import { useState } from 'react'
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from 'flowbite-react'
import { HiOutlineExclamationCircle } from 'react-icons/hi'

function ModalExample() {
  const [openModal, setOpenModal] = useState(false)

  return (
    <>
      <Button onClick={() => setOpenModal(true)}>Open modal</Button>

      <Modal show={openModal} onClose={() => setOpenModal(false)}>
        <ModalHeader>Terms of Service</ModalHeader>
        <ModalBody>
          <div className="space-y-6">
            <p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
              With less than a month to go before the European Union enacts new consumer privacy laws
              for its citizens, companies around the world are updating their terms of service.
            </p>
            <p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
              The European Union's General Data Protection Regulation (G.D.P.R.) goes into effect on
              May 25 and is meant to ensure a common set of data rights.
            </p>
          </div>
        </ModalBody>
        <ModalFooter>
          <Button onClick={() => setOpenModal(false)}>I accept</Button>
          <Button color="gray" onClick={() => setOpenModal(false)}>
            Decline
          </Button>
        </ModalFooter>
      </Modal>
    </>
  )
}

// Confirmation Modal
function DeleteConfirmModal() {
  const [openModal, setOpenModal] = useState(false)

  return (
    <>
      <Button color="failure" onClick={() => setOpenModal(true)}>
        Delete account
      </Button>

      <Modal show={openModal} size="md" onClose={() => setOpenModal(false)} popup>
        <ModalHeader />
        <ModalBody>
          <div className="text-center">
            <HiOutlineExclamationCircle className="mx-auto mb-4 h-14 w-14 text-gray-400 dark:text-gray-200" />
            <h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
              Are you sure you want to delete this product?
            </h3>
            <div className="flex justify-center gap-4">
              <Button color="failure" onClick={() => setOpenModal(false)}>
                Yes, I'm sure
              </Button>
              <Button color="gray" onClick={() => setOpenModal(false)}>
                No, cancel
              </Button>
            </div>
          </div>
        </ModalBody>
      </Modal>
    </>
  )
}

// Różne rozmiary
function SizedModals() {
  const [size, setSize] = useState('md')
  const [openModal, setOpenModal] = useState(false)

  return (
    <>
      <div className="flex flex-wrap gap-4">
        {['sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map((s) => (
          <Button
            key={s}
            onClick={() => {
              setSize(s)
              setOpenModal(true)
            }}
          >
            {s}
          </Button>
        ))}
      </div>

      <Modal show={openModal} size={size} onClose={() => setOpenModal(false)}>
        <ModalHeader>Modal {size}</ModalHeader>
        <ModalBody>
          <p>This is a {size} modal.</p>
        </ModalBody>
      </Modal>
    </>
  )
}

Carousel

Code
TypeScript
import { Carousel } from 'flowbite-react'

function CarouselExample() {
  return (
    <div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
      <Carousel>
        <img src="/carousel-1.svg" alt="Slide 1" />
        <img src="/carousel-2.svg" alt="Slide 2" />
        <img src="/carousel-3.svg" alt="Slide 3" />
        <img src="/carousel-4.svg" alt="Slide 4" />
        <img src="/carousel-5.svg" alt="Slide 5" />
      </Carousel>
    </div>
  )
}

// Bez kontrolek
function CarouselWithoutControls() {
  return (
    <div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
      <Carousel slide={false}>
        <img src="/carousel-1.svg" alt="Slide 1" />
        <img src="/carousel-2.svg" alt="Slide 2" />
        <img src="/carousel-3.svg" alt="Slide 3" />
      </Carousel>
    </div>
  )
}

// Z custom indicators
function CarouselCustomIndicators() {
  return (
    <div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
      <Carousel
        indicators={false}
        leftControl={
          <span className="group-hover:bg-white/50 group-focus:outline-none group-focus:ring-4 group-focus:ring-white">
          </span>
        }
        rightControl={
          <span className="group-hover:bg-white/50 group-focus:outline-none group-focus:ring-4 group-focus:ring-white">
          </span>
        }
      >
        <img src="/carousel-1.svg" alt="Slide 1" />
        <img src="/carousel-2.svg" alt="Slide 2" />
      </Carousel>
    </div>
  )
}

// Auto-slide
function AutoSlideCarousel() {
  return (
    <div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
      <Carousel slideInterval={3000}>
        <img src="/carousel-1.svg" alt="Slide 1" />
        <img src="/carousel-2.svg" alt="Slide 2" />
        <img src="/carousel-3.svg" alt="Slide 3" />
      </Carousel>
    </div>
  )
}

Sidebar

Code
TypeScript
import { Badge, Sidebar, SidebarCTA, SidebarCollapse, SidebarItem, SidebarItemGroup, SidebarItems, SidebarLogo } from 'flowbite-react'
import {
  HiArrowSmRight,
  HiChartPie,
  HiInbox,
  HiShoppingBag,
  HiTable,
  HiUser,
} from 'react-icons/hi'

function SidebarExample() {
  return (
    <Sidebar aria-label="Default sidebar example">
      <SidebarItems>
        <SidebarItemGroup>
          <SidebarItem href="#" icon={HiChartPie}>
            Dashboard
          </SidebarItem>
          <SidebarItem href="#" icon={HiInbox} label="3">
            Inbox
          </SidebarItem>
          <SidebarItem href="#" icon={HiUser}>
            Users
          </SidebarItem>
          <SidebarItem href="#" icon={HiShoppingBag}>
            Products
          </SidebarItem>
          <SidebarItem href="#" icon={HiArrowSmRight}>
            Sign In
          </SidebarItem>
          <SidebarItem href="#" icon={HiTable}>
            Sign Up
          </SidebarItem>
        </SidebarItemGroup>
      </SidebarItems>
    </Sidebar>
  )
}

// Z collapse (submenu)
function SidebarWithCollapse() {
  return (
    <Sidebar aria-label="Sidebar with multi-level dropdown">
      <SidebarItems>
        <SidebarItemGroup>
          <SidebarItem href="#" icon={HiChartPie}>
            Dashboard
          </SidebarItem>
          <SidebarCollapse icon={HiShoppingBag} label="E-commerce">
            <SidebarItem href="#">Products</SidebarItem>
            <SidebarItem href="#">Billing</SidebarItem>
            <SidebarItem href="#">Invoice</SidebarItem>
          </SidebarCollapse>
          <SidebarItem href="#" icon={HiInbox}>
            Inbox
          </SidebarItem>
          <SidebarItem href="#" icon={HiUser}>
            Users
          </SidebarItem>
        </SidebarItemGroup>
      </SidebarItems>
    </Sidebar>
  )
}

// Z logo
function SidebarWithLogo() {
  return (
    <Sidebar aria-label="Sidebar with logo">
      <SidebarLogo href="#" img="/logo.svg" imgAlt="Flowbite logo">
        Flowbite
      </SidebarLogo>
      <SidebarItems>
        <SidebarItemGroup>
          <SidebarItem href="#" icon={HiChartPie}>
            Dashboard
          </SidebarItem>
          <SidebarItem href="#" icon={HiUser}>
            Users
          </SidebarItem>
        </SidebarItemGroup>
      </SidebarItems>
    </Sidebar>
  )
}

// Z CTA
function SidebarWithCTA() {
  return (
    <Sidebar aria-label="Sidebar with CTA">
      <SidebarItems>
        <SidebarItemGroup>
          <SidebarItem href="#" icon={HiChartPie}>
            Dashboard
          </SidebarItem>
        </SidebarItemGroup>
      </SidebarItems>
      <SidebarCTA>
        <div className="mb-3 flex items-center">
          <Badge color="warning">Beta</Badge>
          <button
            aria-label="Close"
            className="-m-1.5 ml-auto inline-flex h-6 w-6 rounded-lg bg-gray-100 p-1 text-cyan-900 hover:bg-gray-200"
            type="button"
          >
            <HiX className="h-4 w-4" />
          </button>
        </div>
        <p className="mb-3 text-sm text-cyan-900">
          Preview the new Flowbite dashboard navigation!
        </p>
        <a
          className="text-sm text-cyan-900 underline hover:text-cyan-800"
          href="#"
        >
          Turn on now
        </a>
      </SidebarCTA>
    </Sidebar>
  )
}

Drawer (Offcanvas)

Code
TypeScript
import { useState } from 'react'
import { Button, Drawer, DrawerHeader, DrawerItems } from 'flowbite-react'

function DrawerExample() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Open drawer</Button>

      <Drawer open={isOpen} onClose={() => setIsOpen(false)}>
        <DrawerHeader title="Drawer" />
        <DrawerItems>
          <p className="mb-6 text-sm text-gray-500 dark:text-gray-400">
            Supercharge your hiring by taking advantage of our limited-time sale.
          </p>
          <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
            <Button>Learn more</Button>
            <Button color="gray" outline>
              Get access
            </Button>
          </div>
        </DrawerItems>
      </Drawer>
    </>
  )
}

// Drawer z różnych stron
function DrawerPositions() {
  const [leftOpen, setLeftOpen] = useState(false)
  const [rightOpen, setRightOpen] = useState(false)
  const [topOpen, setTopOpen] = useState(false)
  const [bottomOpen, setBottomOpen] = useState(false)

  return (
    <div className="flex gap-4">
      <Button onClick={() => setLeftOpen(true)}>Left</Button>
      <Drawer open={leftOpen} onClose={() => setLeftOpen(false)} position="left">
        <DrawerHeader title="Left Drawer" />
        <DrawerItems>
          <p>Content from the left</p>
        </DrawerItems>
      </Drawer>

      <Button onClick={() => setRightOpen(true)}>Right</Button>
      <Drawer open={rightOpen} onClose={() => setRightOpen(false)} position="right">
        <DrawerHeader title="Right Drawer" />
        <DrawerItems>
          <p>Content from the right</p>
        </DrawerItems>
      </Drawer>

      <Button onClick={() => setTopOpen(true)}>Top</Button>
      <Drawer open={topOpen} onClose={() => setTopOpen(false)} position="top">
        <DrawerHeader title="Top Drawer" />
        <DrawerItems>
          <p>Content from the top</p>
        </DrawerItems>
      </Drawer>

      <Button onClick={() => setBottomOpen(true)}>Bottom</Button>
      <Drawer open={bottomOpen} onClose={() => setBottomOpen(false)} position="bottom">
        <DrawerHeader title="Bottom Drawer" />
        <DrawerItems>
          <p>Content from the bottom</p>
        </DrawerItems>
      </Drawer>
    </div>
  )
}

Datepicker

Code
TypeScript
import { Datepicker } from 'flowbite-react'

function DatepickerExamples() {
  return (
    <div className="flex flex-col gap-4">
      {/* Podstawowy */}
      <Datepicker />

      {/* Z domyślną datą */}
      <Datepicker defaultValue={new Date(2024, 0, 15)} />

      {/* Z minimalną i maksymalną datą */}
      <Datepicker
        minDate={new Date(2024, 0, 1)}
        maxDate={new Date(2024, 11, 31)}
      />

      {/* Inline */}
      <Datepicker inline />

      {/* Z tytułem */}
      <Datepicker title="Select Date" />

      {/* Auto hide */}
      <Datepicker autoHide={false} />

      {/* Controlled */}
      <Datepicker
        onChange={(date) => console.log(date)}
      />

      {/* Z labelami */}
      <Datepicker
        showClearButton
        showTodayButton
        labelClearButton="Clear"
        labelTodayButton="Today"
      />

      {/* Różne języki */}
      <Datepicker language="pl-PL" />

      {/* Tydzień zaczyna się od poniedziałku */}
      <Datepicker weekStart={1} />
    </div>
  )
}

Dropdown

Code
TypeScript
import { Dropdown, DropdownDivider, DropdownHeader, DropdownItem } from 'flowbite-react'

function DropdownExamples() {
  return (
    <div className="flex gap-4">
      {/* Podstawowy */}
      <Dropdown label="Dropdown">
        <DropdownItem>Dashboard</DropdownItem>
        <DropdownItem>Settings</DropdownItem>
        <DropdownItem>Earnings</DropdownItem>
        <DropdownItem>Sign out</DropdownItem>
      </Dropdown>

      {/* Z dividerem */}
      <Dropdown label="Actions">
        <DropdownItem>Edit</DropdownItem>
        <DropdownItem>Export</DropdownItem>
        <DropdownDivider />
        <DropdownItem>Delete</DropdownItem>
      </Dropdown>

      {/* Z headerem */}
      <Dropdown label="User menu">
        <DropdownHeader>
          <span className="block text-sm">John Doe</span>
          <span className="block truncate text-sm font-medium">
            john@example.com
          </span>
        </DropdownHeader>
        <DropdownItem>Profile</DropdownItem>
        <DropdownItem>Settings</DropdownItem>
        <DropdownDivider />
        <DropdownItem>Sign out</DropdownItem>
      </Dropdown>

      {/* Z ikonami */}
      <Dropdown label="Options">
        <DropdownItem icon={HiViewGrid}>Dashboard</DropdownItem>
        <DropdownItem icon={HiCog}>Settings</DropdownItem>
        <DropdownItem icon={HiCurrencyDollar}>Earnings</DropdownItem>
        <DropdownDivider />
        <DropdownItem icon={HiLogout}>Sign out</DropdownItem>
      </Dropdown>

      {/* Inline (bez border) */}
      <Dropdown inline label="Inline dropdown">
        <DropdownItem>Item 1</DropdownItem>
        <DropdownItem>Item 2</DropdownItem>
      </Dropdown>

      {/* Różne rozmiary */}
      <Dropdown label="Small" size="sm">
        <DropdownItem>Item</DropdownItem>
      </Dropdown>

      <Dropdown label="Large" size="lg">
        <DropdownItem>Item</DropdownItem>
      </Dropdown>

      {/* Różne pozycje */}
      <Dropdown label="Top" placement="top">
        <DropdownItem>Opens at top</DropdownItem>
      </Dropdown>

      <Dropdown label="Left" placement="left">
        <DropdownItem>Opens at left</DropdownItem>
      </Dropdown>
    </div>
  )
}

Accordion

Code
TypeScript
import { Accordion, AccordionContent, AccordionPanel, AccordionTitle } from 'flowbite-react'

function AccordionExample() {
  return (
    <Accordion>
      <AccordionPanel>
        <AccordionTitle>What is Flowbite?</AccordionTitle>
        <AccordionContent>
          <p className="mb-2 text-gray-500 dark:text-gray-400">
            Flowbite is an open-source library of interactive components built on top of Tailwind CSS.
          </p>
          <p className="text-gray-500 dark:text-gray-400">
            Check out the{' '}
            <a
              href="https://flowbite.com/docs/getting-started/introduction/"
              className="text-cyan-600 hover:underline dark:text-cyan-500"
            >
              getting started guide
            </a>{' '}
            and start building websites.
          </p>
        </AccordionContent>
      </AccordionPanel>
      <AccordionPanel>
        <AccordionTitle>Is there a Figma file?</AccordionTitle>
        <AccordionContent>
          <p className="mb-2 text-gray-500 dark:text-gray-400">
            Yes! You can get the Figma file in Flowbite Pro.
          </p>
        </AccordionContent>
      </AccordionPanel>
      <AccordionPanel>
        <AccordionTitle>What frameworks are supported?</AccordionTitle>
        <AccordionContent>
          <p className="mb-2 text-gray-500 dark:text-gray-400">
            React, Vue, Svelte, Angular, and vanilla JavaScript.
          </p>
        </AccordionContent>
      </AccordionPanel>
    </Accordion>
  )
}

// Zawsze otwarte (wszystkie panele)
function AccordionAlwaysOpen() {
  return (
    <Accordion alwaysOpen>
      <AccordionPanel>
        <AccordionTitle>Panel 1</AccordionTitle>
        <AccordionContent>
          <p>Content 1</p>
        </AccordionContent>
      </AccordionPanel>
      <AccordionPanel>
        <AccordionTitle>Panel 2</AccordionTitle>
        <AccordionContent>
          <p>Content 2</p>
        </AccordionContent>
      </AccordionPanel>
    </Accordion>
  )
}

// Bez border
function AccordionFlush() {
  return (
    <Accordion flush>
      <AccordionPanel>
        <AccordionTitle>Panel without borders</AccordionTitle>
        <AccordionContent>
          <p>Cleaner look</p>
        </AccordionContent>
      </AccordionPanel>
    </Accordion>
  )
}

Table

Code
TypeScript
import { Checkbox, Table, TableBody, TableCell, TableHead, TableHeadCell, TableRow } from 'flowbite-react'

function TableExample() {
  return (
    <div className="overflow-x-auto">
      <Table>
        <TableHead>
          <TableHeadCell className="p-4">
            <Checkbox />
          </TableHeadCell>
          <TableHeadCell>Product name</TableHeadCell>
          <TableHeadCell>Color</TableHeadCell>
          <TableHeadCell>Category</TableHeadCell>
          <TableHeadCell>Price</TableHeadCell>
          <TableHeadCell>
            <span className="sr-only">Edit</span>
          </TableHeadCell>
        </TableHead>
        <TableBody className="divide-y">
          <TableRow className="bg-white dark:border-gray-700 dark:bg-gray-800">
            <TableCell className="p-4">
              <Checkbox />
            </TableCell>
            <TableCell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
              Apple MacBook Pro 17"
            </TableCell>
            <TableCell>Sliver</TableCell>
            <TableCell>Laptop</TableCell>
            <TableCell>$2999</TableCell>
            <TableCell>
              <a href="#" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
                Edit
              </a>
            </TableCell>
          </TableRow>
          <TableRow className="bg-white dark:border-gray-700 dark:bg-gray-800">
            <TableCell className="p-4">
              <Checkbox />
            </TableCell>
            <TableCell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
              Microsoft Surface Pro
            </TableCell>
            <TableCell>White</TableCell>
            <TableCell>Laptop PC</TableCell>
            <TableCell>$1999</TableCell>
            <TableCell>
              <a href="#" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
                Edit
              </a>
            </TableCell>
          </TableRow>
          <TableRow className="bg-white dark:border-gray-700 dark:bg-gray-800">
            <TableCell className="p-4">
              <Checkbox />
            </TableCell>
            <TableCell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
              Magic Mouse 2
            </TableCell>
            <TableCell>Black</TableCell>
            <TableCell>Accessories</TableCell>
            <TableCell>$99</TableCell>
            <TableCell>
              <a href="#" className="font-medium text-cyan-600 hover:underline dark:text-cyan-500">
                Edit
              </a>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </div>
  )
}

// Striped
function StripedTable() {
  return (
    <Table striped>
      <TableHead>
        <TableHeadCell>Name</TableHeadCell>
        <TableHeadCell>Email</TableHeadCell>
      </TableHead>
      <TableBody className="divide-y">
        <TableRow>
          <TableCell>John Doe</TableCell>
          <TableCell>john@example.com</TableCell>
        </TableRow>
        <TableRow>
          <TableCell>Jane Smith</TableCell>
          <TableCell>jane@example.com</TableCell>
        </TableRow>
      </TableBody>
    </Table>
  )
}

// Hoverable
function HoverableTable() {
  return (
    <Table hoverable>
      <TableHead>
        <TableHeadCell>Product</TableHeadCell>
        <TableHeadCell>Price</TableHeadCell>
      </TableHead>
      <TableBody className="divide-y">
        <TableRow className="bg-white dark:border-gray-700 dark:bg-gray-800">
          <TableCell>Product 1</TableCell>
          <TableCell>$99</TableCell>
        </TableRow>
        <TableRow className="bg-white dark:border-gray-700 dark:bg-gray-800">
          <TableCell>Product 2</TableCell>
          <TableCell>$149</TableCell>
        </TableRow>
      </TableBody>
    </Table>
  )
}

Forms

Code
TypeScript
import { Button, Checkbox, FileInput, Label, Radio, RangeSlider, Select, TextInput, Textarea, ToggleSwitch } from 'flowbite-react'
import { HiMail, HiLockClosed } from 'react-icons/hi'

function FormExamples() {
  return (
    <form className="flex max-w-md flex-col gap-4">
      {/* Text Input */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="email" value="Your email" />
        </div>
        <TextInput
          id="email"
          type="email"
          placeholder="name@flowbite.com"
          required
          icon={HiMail}
        />
      </div>

      {/* Password */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="password" value="Your password" />
        </div>
        <TextInput
          id="password"
          type="password"
          required
          icon={HiLockClosed}
        />
      </div>

      {/* Input sizes */}
      <TextInput sizing="sm" placeholder="Small" />
      <TextInput sizing="md" placeholder="Medium (default)" />
      <TextInput sizing="lg" placeholder="Large" />

      {/* Input with helper text */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="username" value="Username" />
        </div>
        <TextInput
          id="username"
          placeholder="Bonnie"
          helperText="We'll never share your details."
        />
      </div>

      {/* Input with validation */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="success" value="Success input" color="success" />
        </div>
        <TextInput
          id="success"
          color="success"
          helperText={
            <span className="font-medium">Well done!</span>
          }
        />
      </div>

      <div>
        <div className="mb-2 block">
          <Label htmlFor="failure" value="Error input" color="failure" />
        </div>
        <TextInput
          id="failure"
          color="failure"
          helperText={
            <span className="font-medium">Oops!</span>
          }
        />
      </div>

      {/* Textarea */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="comment" value="Your message" />
        </div>
        <Textarea
          id="comment"
          placeholder="Leave a comment..."
          rows={4}
        />
      </div>

      {/* Select */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="countries" value="Select your country" />
        </div>
        <Select id="countries" required>
          <option>United States</option>
          <option>Canada</option>
          <option>France</option>
          <option>Germany</option>
        </Select>
      </div>

      {/* Checkbox */}
      <div className="flex items-center gap-2">
        <Checkbox id="remember" />
        <Label htmlFor="remember">Remember me</Label>
      </div>

      {/* Radio */}
      <fieldset className="flex max-w-md flex-col gap-4">
        <legend className="mb-4">Choose your delivery</legend>
        <div className="flex items-center gap-2">
          <Radio id="standard" name="delivery" value="standard" defaultChecked />
          <Label htmlFor="standard">Standard (5-7 days)</Label>
        </div>
        <div className="flex items-center gap-2">
          <Radio id="express" name="delivery" value="express" />
          <Label htmlFor="express">Express (1-2 days)</Label>
        </div>
      </fieldset>

      {/* File Input */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="file" value="Upload file" />
        </div>
        <FileInput id="file" />
      </div>

      {/* Toggle Switch */}
      <ToggleSwitch
        checked={true}
        label="Enable notifications"
        onChange={() => {}}
      />

      {/* Range Slider */}
      <div>
        <div className="mb-2 block">
          <Label htmlFor="default-range" value="Default range" />
        </div>
        <RangeSlider id="default-range" />
      </div>

      <Button type="submit">Submit</Button>
    </form>
  )
}

Card

Code
TypeScript
import { Button, Card } from 'flowbite-react'

function CardExamples() {
  return (
    <div className="flex flex-wrap gap-4">
      {/* Podstawowa karta */}
      <Card className="max-w-sm">
        <h5 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          Noteworthy technology acquisitions 2021
        </h5>
        <p className="font-normal text-gray-700 dark:text-gray-400">
          Here are the biggest enterprise technology acquisitions of 2021.
        </p>
      </Card>

      {/* Karta z przyciskiem */}
      <Card className="max-w-sm">
        <h5 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          Noteworthy technology acquisitions 2021
        </h5>
        <p className="font-normal text-gray-700 dark:text-gray-400">
          Here are the biggest enterprise technology acquisitions.
        </p>
        <Button>
          Read more
          <svg
            className="-mr-1 ml-2 h-4 w-4"
            fill="currentColor"
            viewBox="0 0 20 20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              fillRule="evenodd"
              d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
              clipRule="evenodd"
            />
          </svg>
        </Button>
      </Card>

      {/* Karta z obrazkiem */}
      <Card
        className="max-w-sm"
        imgAlt="Meaningful alt text"
        imgSrc="/card-image.jpg"
      >
        <h5 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          Noteworthy technology acquisitions 2021
        </h5>
        <p className="font-normal text-gray-700 dark:text-gray-400">
          Here are the biggest enterprise technology acquisitions.
        </p>
      </Card>

      {/* Horizontal card */}
      <Card
        className="max-w-sm"
        imgAlt="Meaningful alt text"
        imgSrc="/card-image.jpg"
        horizontal
      >
        <h5 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          Horizontal card
        </h5>
        <p className="font-normal text-gray-700 dark:text-gray-400">
          This is a horizontal card layout.
        </p>
      </Card>

      {/* Decorative image */}
      <Card
        className="max-w-sm"
        renderImage={() => (
          <img
            width={500}
            height={500}
            src="/card-image.jpg"
            alt="image 1"
          />
        )}
      >
        <h5 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          Custom image render
        </h5>
      </Card>
    </div>
  )
}

Toast

Code
TypeScript
import { Button, Toast, ToastToggle } from 'flowbite-react'
import { HiCheck, HiExclamation, HiX, HiFire } from 'react-icons/hi'

function ToastExamples() {
  return (
    <div className="flex flex-col gap-4">
      {/* Success toast */}
      <Toast>
        <div className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-500 dark:bg-green-800 dark:text-green-200">
          <HiCheck className="h-5 w-5" />
        </div>
        <div className="ml-3 text-sm font-normal">Item moved successfully.</div>
        <ToastToggle />
      </Toast>

      {/* Error toast */}
      <Toast>
        <div className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-red-100 text-red-500 dark:bg-red-800 dark:text-red-200">
          <HiX className="h-5 w-5" />
        </div>
        <div className="ml-3 text-sm font-normal">Item has been deleted.</div>
        <ToastToggle />
      </Toast>

      {/* Warning toast */}
      <Toast>
        <div className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-orange-100 text-orange-500 dark:bg-orange-700 dark:text-orange-200">
          <HiExclamation className="h-5 w-5" />
        </div>
        <div className="ml-3 text-sm font-normal">Improve password difficulty.</div>
        <ToastToggle />
      </Toast>

      {/* Custom icon */}
      <Toast>
        <div className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-cyan-100 text-cyan-500 dark:bg-cyan-800 dark:text-cyan-200">
          <HiFire className="h-5 w-5" />
        </div>
        <div className="ml-3 text-sm font-normal">
          Set yourself free.
        </div>
        <ToastToggle />
      </Toast>

      {/* Interactive toast */}
      <Toast>
        <div className="flex items-start">
          <div className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-blue-100 text-blue-500 dark:bg-blue-900 dark:text-blue-300">
            <HiExclamation className="h-5 w-5" />
          </div>
          <div className="ml-3 text-sm font-normal">
            <span className="mb-1 text-sm font-semibold text-gray-900 dark:text-white">
              Update available
            </span>
            <div className="mb-2 text-sm font-normal">
              A new software version is available for download.
            </div>
            <div className="flex gap-2">
              <Button size="xs">Update</Button>
              <Button size="xs" color="light">Not now</Button>
            </div>
          </div>
          <ToastToggle />
        </div>
      </Toast>
    </div>
  )
}

Flowbite Blocks

Flowbite oferuje 450+ gotowych bloków (sekcji stron), które można skopiować i użyć. Oto kategorie:

Dostępne kategorie bloków:

  • Marketing: Hero, Feature, CTA, Content, Testimonial, Blog, Newsletter, Contact
  • Application: Sidebar, Navbar, Footer, CRUD tables, User cards
  • Publisher: Article, Blog post, Comment sections
  • E-commerce: Product cards, Cart, Checkout, Pricing

Przykład Hero Block (Copy-paste HTML)

Code
HTML
<section class="bg-white dark:bg-gray-900">
    <div class="grid max-w-screen-xl px-4 py-8 mx-auto lg:gap-8 xl:gap-0 lg:py-16 lg:grid-cols-12">
        <div class="mr-auto place-self-center lg:col-span-7">
            <h1 class="max-w-2xl mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl xl:text-6xl dark:text-white">
                Payments tool for software companies
            </h1>
            <p class="max-w-2xl mb-6 font-light text-gray-500 lg:mb-8 md:text-lg lg:text-xl dark:text-gray-400">
                From checkout to global sales tax compliance, companies around the world use Flowbite to simplify their payment stack.
            </p>
            <a href="#" class="inline-flex items-center justify-center px-5 py-3 mr-3 text-base font-medium text-center text-white rounded-lg bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:focus:ring-primary-900">
                Get started
                <svg class="w-5 h-5 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                    <path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
                </svg>
            </a>
            <a href="#" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-center text-gray-900 border border-gray-300 rounded-lg hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 dark:text-white dark:border-gray-700 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
                Speak to Sales
            </a>
        </div>
        <div class="hidden lg:mt-0 lg:col-span-5 lg:flex">
            <img src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/hero/phone-mockup.png" alt="mockup">
        </div>
    </div>
</section>

Przykład Pricing Block

Code
HTML
<section class="bg-white dark:bg-gray-900">
  <div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
    <div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
      <h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">
        Designed for business teams like yours
      </h2>
      <p class="mb-5 font-light text-gray-500 sm:text-xl dark:text-gray-400">
        Here at Flowbite we focus on markets where technology, innovation, and capital can unlock long-term value.
      </p>
    </div>
    <div class="space-y-8 lg:grid lg:grid-cols-3 sm:gap-6 xl:gap-10 lg:space-y-0">
      <!-- Pricing Card -->
      <div class="flex flex-col p-6 mx-auto max-w-lg text-center text-gray-900 bg-white rounded-lg border border-gray-100 shadow dark:border-gray-600 xl:p-8 dark:bg-gray-800 dark:text-white">
        <h3 class="mb-4 text-2xl font-semibold">Starter</h3>
        <p class="font-light text-gray-500 sm:text-lg dark:text-gray-400">
          Best option for personal use & for your next project.
        </p>
        <div class="flex justify-center items-baseline my-8">
          <span class="mr-2 text-5xl font-extrabold">$29</span>
          <span class="text-gray-500 dark:text-gray-400">/month</span>
        </div>
        <!-- List -->
        <ul role="list" class="mb-8 space-y-4 text-left">
          <li class="flex items-center space-x-3">
            <svg class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
            </svg>
            <span>Individual configuration</span>
          </li>
          <li class="flex items-center space-x-3">
            <svg class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
            </svg>
            <span>No setup, or hidden fees</span>
          </li>
        </ul>
        <a href="#" class="text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:ring-primary-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:text-white dark:focus:ring-primary-900">
          Get started
        </a>
      </div>
      <!-- More cards... -->
    </div>
  </div>
</section>

Dark Mode

Flowbite w pełni wspiera dark mode poprzez klasę dark::

JStailwind.config.js
JavaScript
// tailwind.config.js
module.exports = {
  darkMode: 'class',
  // ...
}
Code
TypeScript
// Przełącznik dark mode
import { useState, useEffect } from 'react'
import { DarkThemeToggle } from 'flowbite-react'

function DarkModeToggle() {
  return (
    <DarkThemeToggle />
  )
}

// Lub własna implementacja
function CustomDarkModeToggle() {
  const [isDark, setIsDark] = useState(false)

  useEffect(() => {
    if (isDark) {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
  }, [isDark])

  return (
    <button onClick={() => setIsDark(!isDark)}>
      {isDark ? '🌙' : '☀️'}
    </button>
  )
}

Flowbite Icons

Flowbite oferuje też zestaw ponad 750 darmowych ikon SVG: 411 w wersji konturowej i 340 w wypełnionej. Surowe pliki SVG leżą w pakiecie flowbite-icons, a gotowe komponenty dla Reacta w osobnym pakiecie flowbite-react-icons, podzielonym na dwa punkty wejścia.

Code
Bash
npm install flowbite-react-icons
Code
TypeScript
import { Search, User, Bell } from 'flowbite-react-icons/outline'
import { Search as SearchSolid, User as UserSolid, Bell as BellSolid } from 'flowbite-react-icons/solid'

function IconsExample() {
  return (
    <div className="flex gap-4">
      {/* Ikony konturowe */}
      <Search className="w-6 h-6" />
      <User className="w-6 h-6" />
      <Bell className="w-6 h-6" />

      {/* Ikony wypełnione */}
      <SearchSolid className="w-6 h-6" />
      <UserSolid className="w-6 h-6" />
      <BellSolid className="w-6 h-6" />
    </div>
  )
}

Warstwa darmowa i płatna

Podział jest tu wyraźny i warto go rozumieć przed rozpoczęciem projektu, bo decyduje o tym, ile faktycznie dostaniesz za darmo.

Rdzeń biblioteki jest na licencji MIT i obejmuje komponenty: przyciski, formularze, tabele, okna modalne, listy rozwijane i resztę elementów, z których buduje się interfejs. Tego wystarczy do zbudowania działającej aplikacji i nie wiąże się z żadną opłatą ani obowiązkiem wskazania źródła.

Płatna jest warstwa wyżej: gotowe bloki, czyli złożone sekcje stron, kompletne szablony paneli i sklepów oraz pliki projektowe w Figmie. To jest ta sama różnica co między klockiem a gotową ścianą: darmowe komponenty dają Ci elementy, płatne bloki dają ułożoną z nich stronę.

Konkretnych kwot świadomie nie podaję, bo strona z cennikiem renderuje się dopiero w przeglądarce i nie da się jej rzetelnie zacytować, a zestawienia krążące po sieci różnią się między sobą. Model jest jednorazowy z dożywotnim dostępem, więc nie ma tu abonamentu, ale aktualną wartość sprawdź bezpośrednio przed zakupem.

Pytanie, które warto sobie zadać przed decyzją, brzmi: czy brakuje Ci elementów, czy pomysłu na ich ułożenie. W pierwszym przypadku warstwa darmowa wystarczy. W drugim płacisz za kompozycję i za czas, którego nie spędzisz na dobieraniu odstępów, a to bywa warte swojej ceny przy stronie, którą trzeba postawić w tydzień.

FAQ - Najczęściej Zadawane Pytania

Czy Flowbite wymaga JavaScript?

Dla statycznych komponentów (buttons, cards, typography) - nie. Dla interaktywnych (dropdown, modal, datepicker) - tak.

Czy Flowbite działa z Next.js?

Tak! flowbite-react jest w pełni kompatybilny z Next.js App Router.

Jak dodać własne style do komponentów?

Użyj prop className lub theme:

Code
TypeScript
<Button className="my-custom-class">
  Custom styled
</Button>

// Lub z theme
<Button theme={{ base: "my-custom-base-class" }}>
  Theme styled
</Button>

Czy mogę użyć Flowbite z Vite?

Tak, Flowbite działa ze wszystkimi bundlerami - Vite, Webpack, esbuild, etc.

Jaka jest różnica między Flowbite a DaisyUI?

Flowbite oferuje JavaScript komponenty dla interaktywności (modal, dropdown), DaisyUI to tylko CSS. Flowbite wspiera wiele frameworków, DaisyUI działa wszędzie gdzie jest Tailwind.

Czy komponenty są dostępne dla czytników ekranu?

Częściowo, i warto to sprawdzić samodzielnie dla elementów, na których Ci zależy. Znaczniki niosą atrybuty ról, ale zakres obsługi klawiatury i zarządzania fokusem bywa nierówny między komponentami i między wariantami dla różnych frameworków.

Dostępność, czyli o co dopytać przed wyborem

Ta sekcja jest ważniejsza, niż sugeruje jej długość w większości materiałów o bibliotekach komponentów, bo dotyczy rzeczy, której nie widać przy pierwszym wrażeniu.

Biblioteki tego typu dzielą się na dwie grupy. Jedne budują na warstwie zachowań, która została napisana z myślą o dostępności i przetestowana pod tym kątem, i wtedy pułapka fokusa w oknie modalnym, obsługa strzałek w menu i ogłoszenia dla czytnika ekranu przychodzą razem z komponentem. Drugie implementują zachowania samodzielnie, kierując się głównie tym, żeby wyglądało i działało klikalnie.

Flowbite należy do drugiej grupy i to jest świadomy kompromis, a nie zaniedbanie. Zyskujesz niezależność od dodatkowych zależności i możliwość użycia tego samego kodu w czystym HTML, tracisz gwarancję, że każdy komponent zachowa się poprawnie bez myszy.

Praktyczny test zajmuje kilka minut i warto go wykonać na komponentach kluczowych dla Twojego interfejsu. Otwórz okno modalne klawiaturą, sprawdź, czy fokus wszedł do środka, czy tabulator nie wychodzi poza nie, czy klawisz ucieczki zamyka i czy po zamknięciu fokus wrócił na przycisk otwierający. To samo z listą rozwijaną i strzałkami. Jeśli któryś z tych warunków nie jest spełniony, wiesz, ile pracy trzeba dołożyć.

Alternatywą przy projekcie, w którym dostępność jest wymogiem formalnym, jest zestaw budowany na warstwie zachowań, na przykład Headless UI albo Radix UI, z wyglądem złożonym samodzielnie. Kosztuje to więcej pracy na starcie i zwraca się w audycie.

Kod źródłowy i listę komponentów znajdziesz w repozytorium Flowbite, a dokumentację na stronie projektu.