Przejdź do treści
Tworzenie Stron

Prototypy z shadcn/ui komponentami - Kompletny przewodnik

Dowiedz się jak tworzyć profesjonalne prototypy stron internetowych używając shadcn/ui. Praktyczny przewodnik z przykładami kodu i najlepszymi praktykami.

26 stycznia 2025
12 min
Zagor Digital

Szybka odpowiedź

shadcn/ui to biblioteka komponentów React oparta na Radix UI i Tailwind CSS, która umożliwia szybkie tworzenie prototypów poprzez kopiowanie gotowego kodu komponentów bezpośrednio do projektu. W przeciwieństwie do tradycyjnych bibliotek, shadcn/ui daje pełną kontrolę nad kodem, co pozwala na łatwe dostosowanie komponentów do indywidualnych potrzeb bez ograniczeń zewnętrznych zależności.

Wprowadzenie

Tworzenie prototypów stron internetowych jest kluczowym etapem w procesie projektowania. Wybór odpowiednich narzędzi może znacząco przyspieszyć pracę i poprawić jakość końcowego produktu. shadcn/ui wyróżnia się na tle innych bibliotek komponentów unikalnym podejściem - zamiast instalować paczkę npm, kopiujesz kod bezpośrednio do swojego projektu.

W tym przewodniku pokażemy, jak efektywnie wykorzystać shadcn/ui do tworzenia prototypów, od podstawowej konfiguracji po zaawansowane techniki optymalizacji. Dowiesz się również, jak komponenty shadcn/ui są zoptymalizowane pod kątem wyszukiwarek AI, co jest kluczowe w 2025 roku.

Czym jest shadcn/ui i dlaczego warto go używać?

Podstawowa filozofia

shadcn/ui to nie jest tradycyjna biblioteka komponentów. To sposób myślenia o budowaniu interfejsów oparty na trzech filarach:

  1. Open Code - pełny dostęp do kodu źródłowego każdego komponentu
  2. Composition - komponenty zaprojektowane do łączenia w większe struktury
  3. Customization - łatwe dostosowanie bez walki z nadpisywaniem styli

Kluczowe zalety dla prototypowania

  • Brak vendor lock-in - kod należy do Ciebie, nie do biblioteki
  • Pełna kontrola - możesz modyfikować każdy aspekt komponentu
  • TypeScript ready - wszystkie komponenty mają wbudowane typy
  • Accessibility first - dzięki Radix UI wszystko jest zgodne z WCAG
  • Performance optimized - komponenty są lekkie i zoptymalizowane

Rozpoczęcie pracy z shadcn/ui

Wymagania wstępne

Przed rozpoczęciem upewnij się, że masz:

  • Node.js 16.8 lub nowszy
  • React 18 lub nowszy
  • Tailwind CSS skonfigurowany w projekcie
  • TypeScript (opcjonalnie, ale zalecany)

Instalacja i konfiguracja

# Inicjalizacja shadcn/ui w projekcie
npx shadcn-ui@latest init

# Wybierz preferowane opcje:
# - Would you like to use TypeScript? → Yes
# - Which style would you like to use? → Default
# - Which color would you like to use as base color? → Slate
# - Where is your global CSS file? → src/index.css
# - Would you like to use CSS variables for colors? → Yes
# - Where is your tailwind.config.js located? → tailwind.config.js
# - Configure the import alias for components? → src/components
# - Configure the import alias for utils? → src/lib/utils

Struktura projektu

Po inicjalizacji otrzymasz następującą strukturę:

src/
├── components/
│   └── ui/          # Komponenty shadcn/ui
├── lib/
│   └── utils.ts     # Funkcje pomocnicze
└── styles/
    └── globals.css  # Style globalne z zmiennymi CSS

Budowanie prototypów - praktyczne przykłady

1. Prototyp strony głównej

Zacznijmy od stworzenia prototypu strony głównej używając komponentów shadcn/ui:

// src/app/page.tsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"

export default function HomePage() {
  return (
    <div className="container mx-auto py-8">
      {/* Hero Section */}
      <section className="text-center py-20">
        <Badge className="mb-4">Nowość</Badge>
        <h1 className="text-4xl font-bold mb-4">
          Tworzymy strony, które sprzedają
        </h1>
        <p className="text-xl text-muted-foreground mb-8 max-w-2xl mx-auto">
          Wykorzystujemy najnowsze technologie i sprawdzone praktyki UX,
          aby Twoja strona przyciągała i konwertowała klientów.
        </p>
        <div className="flex gap-4 justify-center">
          <Button size="lg">Rozpocznij projekt</Button>
          <Button size="lg" variant="outline">Zobacz portfolio</Button>
        </div>
      </section>

      {/* Features Grid */}
      <section className="grid md:grid-cols-3 gap-6 py-20">
        {features.map((feature, index) => (
          <Card key={index}>
            <CardHeader>
              <CardTitle>{feature.title}</CardTitle>
              <CardDescription>{feature.description}</CardDescription>
            </CardHeader>
            <CardContent>
              <p className="text-sm">{feature.details}</p>
            </CardContent>
          </Card>
        ))}
      </section>
    </div>
  )
}

const features = [
  {
    title: "Responsywny design",
    description: "Idealnie dopasowany do każdego urządzenia",
    details: "Wykorzystujemy mobile-first approach i testujemy na wszystkich popularnych urządzeniach."
  },
  {
    title: "Szybkie ładowanie",
    description: "Optymalizacja Core Web Vitals",
    details: "Stosujemy lazy loading, optymalizację obrazów i code splitting dla maksymalnej wydajności."
  },
  {
    title: "SEO & AI Ready",
    description: "Zoptymalizowane dla wyszukiwarek",
    details: "Strukturalne dane, semantyczny HTML i optymalizacja dla wyszukiwarek AI."
  }
]

2. Zaawansowane techniki kompozycji

Pattern: Compound Components

// components/pricing-card.tsx
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Check } from "lucide-react"

interface PricingCardProps {
  title: string
  price: string
  features: string[]
  highlighted?: boolean
}

export function PricingCard({ title, price, features, highlighted }: PricingCardProps) {
  return (
    <Card className={highlighted ? "border-primary shadow-lg" : ""}>
      <CardHeader>
        <h3 className="text-2xl font-bold">{title}</h3>
        <p className="text-3xl font-bold">{price}<span className="text-sm">/mies</span></p>
      </CardHeader>
      <CardContent>
        <ul className="space-y-2">
          {features.map((feature, index) => (
            <li key={index} className="flex items-center gap-2">
              <Check className="w-4 h-4 text-primary" />
              <span className="text-sm">{feature}</span>
            </li>
          ))}
        </ul>
      </CardContent>
      <CardFooter>
        <Button className="w-full" variant={highlighted ? "default" : "outline"}>
          Wybierz plan
        </Button>
      </CardFooter>
    </Card>
  )
}

3. Optymalizacja wydajności

Dynamic Imports dla dużych komponentów

import dynamic from 'next/dynamic'
import { Skeleton } from "@/components/ui/skeleton"

// Lazy loading dla złożonych komponentów
const DataTable = dynamic(
  () => import('@/components/ui/data-table'),
  {
    loading: () => <Skeleton className="h-[400px] w-full" />,
    ssr: false
  }
)

// Użycie w komponencie
export function Dashboard() {
  return (
    <div>
      <h2>Panel analityczny</h2>
      <DataTable data={analyticsData} />
    </div>
  )
}

Memoizacja dla czystych komponentów

import React from 'react'
import { Card } from "@/components/ui/card"

// Memoizacja zapobiega niepotrzebnym re-renderom
export const StatsCard = React.memo(({ title, value, change }) => {
  return (
    <Card className="p-6">
      <h3 className="text-sm font-medium text-muted-foreground">{title}</h3>
      <p className="text-2xl font-bold">{value}</p>
      <p className={`text-sm ${change > 0 ? 'text-green-600' : 'text-red-600'}`}>
        {change > 0 ? '+' : ''}{change}%
      </p>
    </Card>
  )
})

StatsCard.displayName = 'StatsCard'

Najlepsze praktyki dla polskiego rynku

1. Lokalizacja komponentów

// lib/i18n/translations.ts
export const translations = {
  pl: {
    common: {
      save: "Zapisz",
      cancel: "Anuluj",
      delete: "Usuń",
      edit: "Edytuj",
      loading: "Ładowanie...",
      error: "Wystąpił błąd"
    },
    forms: {
      required: "To pole jest wymagane",
      email: "Wprowadź poprawny adres email",
      phone: "Format: +48 123 456 789"
    }
  }
}

// Użycie w komponencie
import { useTranslation } from '@/lib/i18n'
import { Button } from "@/components/ui/button"

export function SaveButton() {
  const { t } = useTranslation()
  return <Button>{t('common.save')}</Button>
}

2. Integracja z polskimi systemami

// components/nip-input.tsx
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useState } from "react"

export function NIPInput({ onValidated }) {
  const [nip, setNip] = useState("")
  const [error, setError] = useState("")

  const validateNIP = (value: string) => {
    const cleanNIP = value.replace(/[\s-]/g, '')
    
    if (cleanNIP.length !== 10) {
      setError("NIP musi zawierać 10 cyfr")
      return false
    }

    // Walidacja sumy kontrolnej NIP
    const weights = [6, 5, 7, 2, 3, 4, 5, 6, 7]
    let sum = 0
    
    for (let i = 0; i < 9; i++) {
      sum += parseInt(cleanNIP[i]) * weights[i]
    }
    
    const checkDigit = sum % 11
    const isValid = checkDigit === parseInt(cleanNIP[9])
    
    if (!isValid) {
      setError("Nieprawidłowy numer NIP")
      return false
    }
    
    setError("")
    onValidated(cleanNIP)
    return true
  }

  return (
    <div className="space-y-2">
      <Label htmlFor="nip">NIP firmy</Label>
      <Input
        id="nip"
        placeholder="123-456-78-90"
        value={nip}
        onChange={(e) => setNip(e.target.value)}
        onBlur={() => validateNIP(nip)}
      />
      {error && <p className="text-sm text-destructive">{error}</p>}
    </div>
  )
}

Integracja z narzędziami designu

Export z Figma do shadcn/ui

  1. Design Tokens - Ustaw zmienne kolorów w Figma zgodnie z paletą shadcn/ui
  2. Spacing System - Używaj wielokrotności 4px (Tailwind: 1 = 0.25rem)
  3. Typography Scale - Dopasuj do skali Tailwind (text-xs do text-9xl)

Workflow Designer-Developer

// components/design-system/colors.ts
export const colors = {
  // Mapowanie kolorów z Figma do Tailwind
  primary: {
    50: 'hsl(var(--primary-50))',
    100: 'hsl(var(--primary-100))',
    // ... pozostałe odcienie
  },
  // Kolory marki Zagor Digital
  brand: {
    orange: '#FF6B35',
    blue: '#4A90E2',
    dark: '#2C3E50'
  }
}

Optymalizacja dla wyszukiwarek AI

1. Semantyczna struktura

// Używaj semantycznych tagów HTML
export function ArticleCard({ article }) {
  return (
    <article className="border rounded-lg p-6">
      <header>
        <h2 className="text-2xl font-bold">{article.title}</h2>
        <time dateTime={article.date}>{formatDate(article.date)}</time>
      </header>
      <section>
        <p>{article.excerpt}</p>
      </section>
      <footer>
        <Button asChild>
          <a href={article.url}>Czytaj więcej</a>
        </Button>
      </footer>
    </article>
  )
}

2. Strukturalne dane

// components/seo/structured-data.tsx
export function ArticleStructuredData({ article }) {
  const structuredData = {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": article.title,
    "description": article.description,
    "author": {
      "@type": "Organization",
      "name": "Zagor Digital"
    },
    "datePublished": article.publishedAt,
    "dateModified": article.updatedAt,
    "publisher": {
      "@type": "Organization",
      "name": "Zagor Digital",
      "logo": {
        "@type": "ImageObject",
        "url": "https://zagordigital.pl/logo.png"
      }
    }
  }

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
    />
  )
}

Testowanie prototypów

1. Testy jednostkowe

// __tests__/components/pricing-card.test.tsx
import { render, screen } from '@testing-library/react'
import { PricingCard } from '@/components/pricing-card'

describe('PricingCard', () => {
  it('renderuje tytuł i cenę', () => {
    render(
      <PricingCard 
        title="Plan Podstawowy"
        price="299 zł"
        features={['Feature 1', 'Feature 2']}
      />
    )
    
    expect(screen.getByText('Plan Podstawowy')).toBeInTheDocument()
    expect(screen.getByText('299 zł')).toBeInTheDocument()
  })

  it('wyróżnia kartę gdy highlighted=true', () => {
    const { container } = render(
      <PricingCard 
        title="Plan Pro"
        price="599 zł"
        features={[]}
        highlighted={true}
      />
    )
    
    expect(container.firstChild).toHaveClass('border-primary')
  })
})

2. Testy dostępności

// cypress/e2e/accessibility.cy.ts
describe('Testy dostępności', () => {
  it('strona główna spełnia standardy WCAG', () => {
    cy.visit('/')
    cy.injectAxe()
    cy.checkA11y()
  })

  it('nawigacja klawiaturą działa poprawnie', () => {
    cy.visit('/')
    cy.get('body').tab()
    cy.focused().should('have.text', 'Rozpocznij projekt')
    cy.get('body').tab()
    cy.focused().should('have.text', 'Zobacz portfolio')
  })
})

Częste problemy i rozwiązania

Problem 1: Konflikty stylów Tailwind

Rozwiązanie: Używaj narzędzia cn() z shadcn/ui

import { cn } from "@/lib/utils"

export function CustomButton({ className, ...props }) {
  return (
    <Button 
      className={cn(
        "custom-default-styles",
        className
      )} 
      {...props}
    />
  )
}

Problem 2: Hydration errors w Next.js

Rozwiązanie: Używaj suppressHydrationWarning dla dynamicznych treści

export function TimeDisplay() {
  return (
    <time suppressHydrationWarning>
      {new Date().toLocaleTimeString('pl-PL')}
    </time>
  )
}

Problem 3: Wielkość bundla

Rozwiązanie: Importuj tylko potrzebne komponenty

// ❌ Źle - importuje całą bibliotekę ikon
import * as Icons from 'lucide-react'

// ✅ Dobrze - importuje tylko potrzebne ikony
import { Search, Menu, X } from 'lucide-react'

Najczęściej zadawane pytania

Czy shadcn/ui jest darmowy?

Tak, shadcn/ui jest całkowicie darmowy i open source. Możesz używać go w projektach komercyjnych bez żadnych opłat licencyjnych.

Jak shadcn/ui wypada w porównaniu do Material-UI?

shadcn/ui jest lżejszy (brak zewnętrznych zależności) i daje większą kontrolę nad kodem. Material-UI oferuje więcej gotowych komponentów, ale jest cięższy i trudniejszy w customizacji.

Czy mogę używać shadcn/ui bez Tailwind CSS?

Technicznie tak, ale wymaga to przepisania wszystkich styli. shadcn/ui został zaprojektowany z myślą o Tailwind CSS i jego używanie jest silnie zalecane.

Jak często są aktualizowane komponenty?

Komponenty są regularnie aktualizowane, ale ponieważ kod należy do Ciebie, aktualizacje nie są automatyczne. Możesz selektywnie aktualizować komponenty według potrzeb.

Czy shadcn/ui wspiera React Native?

Nie, shadcn/ui jest przeznaczony tylko dla aplikacji webowych. Dla React Native polecamy NativeWind lub Tamagui.

Jak zintegrować shadcn/ui z istniejącym projektem?

Wystarczy uruchomić npx shadcn-ui@latest init w istniejącym projekcie React. Wizard przeprowadzi cię przez proces konfiguracji.

Czy komponenty są zgodne z WCAG?

Tak, wszystkie komponenty bazują na Radix UI, który priorytetowo traktuje dostępność i zgodność z WCAG 2.1.

Podsumowanie i kolejne kroki

Prototypowanie z shadcn/ui to efektywny sposób na szybkie tworzenie profesjonalnych interfejsów. Kluczowe zalety to pełna kontrola nad kodem, świetna wydajność i wbudowana dostępność.

Następne kroki:

  1. Zainstaluj shadcn/ui w swoim projekcie
  2. Stwórz pierwszy prototyp używając podstawowych komponentów
  3. Dostosuj style do swojej marki
  4. Przetestuj dostępność i wydajność

Potrzebujesz pomocy w tworzeniu prototypów dla swojej firmy? Skontaktuj się z nami - zespół Zagor Digital pomoże Ci stworzyć prototyp, który zachwyci Twoich klientów.


Ostatnia aktualizacja: 26 stycznia 2025

Tworzenie Stron WWW

Zamow profesjonalna strone internetowa

Tagi:

shadcn/ui
prototypowanie
komponenty UI
React
Next.js
Tailwind CSS

Udostępnij artykuł:

ZD

Zagor Digital

Eksperci w dziedzinie marketingu internetowego i pozycjonowania lokalnego.

Tworzenie Stron WWW

Zamow profesjonalna strone internetowa

Porady SEO co tydzien

Dolacz do newslettera i otrzymuj sprawdzone strategie pozycjonowania.