Styled Components — CSS in JS, props bilan dinamik stil

~20 daq · React · Stilizatsiya

Muammodan boshlaylik

Tugma komponenti yozyapsiz. Uch xil variant kerak: oddiy, xavfli, o'chirilgan. CSS Modules bilan shunday qilasiz:

<button className={`${styles.btn} ${styles[variant]} ${disabled ? styles.disabled : ''}`}>

className satrini yig'ish uchun ternary, kvadrat qavslar, template literal — hammasi bitta qatorda. Hali size, fullWidth, loading variantlari qo'shilmagan.

Yana bir muammo: stil va mantiq ikki joyda — Button.jsx va Button.module.css. Qaysi klassning qaysi propga bog'liqligini ko'rish uchun ikki faylni birga ochib olishingiz kerak.

Styled Components bu ikki muammoni hal qiladi: stil to'g'ridan-to'g'ri komponent ichida, props orqali dinamik o'zgaradi.


Styled Components nima?

Styled Components — bu CSS ni JavaScript ichida yozish imkonini beradigan kutubxona. Siz oddiy HTML elementidan yoki mavjud komponentdan yangi stilizatsiya qilingan komponent yaratasiz.

Hayotiy o'xshatma: oddiy charm ko'rpa bor, siz uning ustiga maxsus qopqoq tikasiz — qizil, yumshoq, tugmali. Endi bu yangi narsa — ko'rpa emas, o'z nomi bor alohida buyum. Styled Components ham shunday: oddiy <button> dan yangi <PrimaryButton> yaratasiz — uning o'z stiliga ega, alohida komponent.


O'rnatish

npm install styled-components

TypeScript ishlatayotgan bo'lsangiz:

npm install styled-components
npm install -D @types/styled-components

Birinchi styled komponent

import styled from 'styled-components';

// styled.button — <button> elementidan yangi komponent yaratadi
const Button = styled.button`
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: 14px;
  transition: background 0.2s;

  &:hover {
    background: #2563eb;
  }

  &:active {
    transform: scale(0.98);
  }
`;

// Ishlatish — oddiy HTML elementi kabi
function App() {
  return <Button>Saqlash</Button>;
}

styled.button — tagged template literal. Backtick ` ichidagi matn CSS sifatida o'qiladi. & — bu komponentning o'zi, SCSS dagi & ga o'xshash.

Brauzerda Button oddiy <button> bo'ladi, lekin unga noyob klass qo'shilgan: class="sc-abc123 dXqPzL". CSS Modules ga o'xshash mexanizm — nom konflikti yo'q.


Props bilan dinamik stil — asosiy kuch

Bu Styled Components ning eng muhim xususiyati. Props ga qarab stil o'zgaradi.

const Button = styled.button`
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s;

  /* props.variant ga qarab fon rangi o'zgaradi */
  background: ${props => {
    if (props.variant === 'danger')  return '#ef4444';
    if (props.variant === 'success') return '#10b981';
    return '#3b82f6'; // standart — primary
  }};

  color: white;

  /* props.size ga qarab padding va shrift */
  padding: ${props => props.size === 'sm' ? '6px 14px' : '10px 20px'};
  font-size: ${props => props.size === 'sm' ? '13px' : '14px'};

  /* props.fullWidth — boolean prop */
  width: ${props => props.fullWidth ? '100%' : 'auto'};

  /* props.disabled ga qarab opacity */
  opacity: ${props => props.disabled ? 0.5 : 1};
  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};

  &:hover {
    filter: ${props => props.disabled ? 'none' : 'brightness(0.9)'};
  }
`;

// Ishlatish
<Button>Oddiy</Button>
<Button variant="danger">O'chirish</Button>
<Button variant="success" size="sm">Saqlash</Button>
<Button fullWidth>Kirish</Button>
<Button disabled>Bloklangan</Button>

Nima uchun bu qulay? className yig'ish yo'q. Qaysi props qaysi stilga ta'sir qilishini bir joyda ko'rasiz. TypeScript bilan proplar aniq chegaralanadi.


Props ni HTMLga o'tkazmaslik

Styled Components props ni avtomatik HTML elementiga uzatadi. Lekin ba'zi proplar HTML atributi emas — brauzer console da ogohlantirish chiqadi.

// ❌ Muammo: variant, fullWidth HTML da mavjud emas
// Brauzer: Unknown prop `variant` on <button> tag
const Button = styled.button`
  background: ${p => p.variant === 'danger' ? 'red' : 'blue'};
  width: ${p => p.fullWidth ? '100%' : 'auto'};
`;

Yechim — $ prefiksi (styled-components v6 dan beri):

// ✅ $ bilan boshlanadigan props HTMLga o'tmaydi
const Button = styled.button`
  background: ${p => p.$variant === 'danger' ? 'red' : 'blue'};
  width: ${p => p.$fullWidth ? '100%' : 'auto'};
`;

<Button $variant="danger" $fullWidth>O'chirish</Button>

$variant, $fullWidth — faqat styled-components uchun, HTMLga yetib bormaydi.


Mavjud komponentni kengaytirish

Styled Components yordamida mavjud komponentni kengaytirish mumkin — uning stillarini meros olib, ustiga yangi qo'shish:

const Button = styled.button`
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  background: #3b82f6;
  color: white;
`;

// Button stillarini meros oladi, ustiga qo'shadi
const OutlineButton = styled(Button)`
  background: transparent;
  border: 1px solid #3b82f6;
  color: #3b82f6;

  &:hover {
    background: #3b82f6;
    color: white;
  }
`;

const IconButton = styled(Button)`
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
`;
<Button>Asosiy</Button>
<OutlineButton>Chegarali</OutlineButton>
<IconButton>
  <svg>...</svg>
  Ikona bilan
</IconButton>

SCSS dagi @extend ga o'xshaydi — lekin komponent darajasida ishlaydi.


.attrs() — default atributlar

Komponentga default HTML atributlarini bir marta belgilash:

const Input = styled.input.attrs({
  type: 'text',
  autoComplete: 'off',
  spellCheck: false,
})`
  border: 1px solid #d1d5db;
  border-radius: 6px;
  padding: 8px 12px;
  font-size: 14px;
  outline: none;
  width: 100%;

  &:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
  }
`;

// Har safar type="text" yozish shart emas
<Input placeholder="Ism kiriting" />
<Input placeholder="Email" />

Props bilan ham ishlaydi:

const SizedInput = styled.input.attrs(props => ({
  type: props.$type || 'text',
  size: props.$size || 20, // HTML size atributi
}))`
  padding: ${p => p.$size === 'lg' ? '12px 16px' : '8px 12px'};
`;

Animatsiya — keyframes

import styled, { keyframes } from 'styled-components';

// @keyframes o'rniga keyframes helper
const spin = keyframes`
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
`;

const fadeIn = keyframes`
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
`;

const Spinner = styled.div`
  width: 24px;
  height: 24px;
  border: 3px solid #e5e7eb;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: ${spin} 0.8s linear infinite;
`;

const FadeCard = styled.div`
  background: white;
  border-radius: 8px;
  padding: 20px;
  animation: ${fadeIn} 0.3s ease;
`;

Tema (ThemeProvider)

Katta loyihalarda barcha komponentlar uchun bir xil ranglar, shriftlar, masofalarni boshqarish muammosi bor. ThemeProvider yechim beradi:

import { ThemeProvider } from 'styled-components';

const tema = {
  ranglar: {
    primary:  '#3b82f6',
    danger:   '#ef4444',
    success:  '#10b981',
    text:     '#111827',
    textMuted:'#6b7280',
    border:   '#e5e7eb',
    bg:       '#ffffff',
  },
  radius: {
    sm: '4px',
    md: '6px',
    lg: '12px',
  },
  font: {
    sm: '13px',
    md: '14px',
    lg: '16px',
  },
};

// App ni ThemeProvider bilan o'rab olamiz
function App() {
  return (
    <ThemeProvider theme={tema}>
      <Sahifa />
    </ThemeProvider>
  );
}

Endi har qanday styled komponent props.theme orqali temaga kiradi:

const Button = styled.button`
  background: ${p => p.theme.ranglar.primary};
  border-radius: ${p => p.theme.radius.md};
  font-size: ${p => p.theme.font.md};
  color: white;
  padding: 10px 20px;
  border: none;
  cursor: pointer;
`;

const Card = styled.div`
  background: ${p => p.theme.ranglar.bg};
  border: 1px solid ${p => p.theme.ranglar.border};
  border-radius: ${p => p.theme.radius.lg};
  padding: 20px;
`;

const MutedText = styled.p`
  color: ${p => p.theme.ranglar.textMuted};
  font-size: ${p => p.theme.font.sm};
`;

Nima uchun tema kerak? Bir joyda primary rangini o'zgartirsangiz — barcha komponentlarda o'zgaradi. Dark mode qo'shish uchun yangi tema ob'ekti yaratib ThemeProvider ga bering — tamom.


Amaliy misol — to'liq Card komponenti

import styled, { keyframes } from 'styled-components';

const fadeUp = keyframes`
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
`;

const CardWrapper = styled.div`
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  overflow: hidden;
  animation: ${fadeUp} 0.3s ease;
  transition: box-shadow 0.2s, transform 0.2s;

  &:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
    transform: translateY(-2px);
  }
`;

const CardImage = styled.img`
  width: 100%;
  height: 200px;
  object-fit: cover;
  display: block;
`;

const CardBody = styled.div`
  padding: 16px;
`;

const CardTitle = styled.h3`
  font-size: 16px;
  font-weight: 600;
  color: #111827;
  margin-bottom: 6px;
`;

const CardText = styled.p`
  font-size: 14px;
  color: #6b7280;
  line-height: 1.6;
  margin-bottom: 16px;
`;

const CardFooter = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 12px;
  border-top: 1px solid #f3f4f6;
`;

const Tag = styled.span`
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 3px 8px;
  border-radius: 20px;

  /* $color prop ga qarab dinamik stil */
  background: ${p => p.$color ? `${p.$color}18` : '#f3f4f6'};
  color: ${p => p.$color || '#6b7280'};
`;

const ReadButton = styled.button`
  font-size: 13px;
  color: #3b82f6;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  font-weight: 500;

  &:hover {
    text-decoration: underline;
  }
`;

// Ishlatish
function ArticleCard({ article }) {
  return (
    <CardWrapper>
      <CardImage src={article.image} alt={article.title} />
      <CardBody>
        <CardTitle>{article.title}</CardTitle>
        <CardText>{article.excerpt}</CardText>
        <CardFooter>
          <Tag $color="#3b82f6">{article.category}</Tag>
          <ReadButton>O'qish →</ReadButton>
        </CardFooter>
      </CardBody>
    </CardWrapper>
  );
}

Fayl tuzilishi aniq: har bir vizual element o'z nomiga ega komponent. CardImage, CardBody, CardFooter — JSX o'qilganda tuzilish darhol ko'rinadi.


Tez-tez uchraydigan xatolar

Props HTMLga o'tib ketishi

// ❌ variant HTML da mavjud emas — console da ogohlantirish
const Box = styled.div`
  color: ${p => p.variant === 'red' ? 'red' : 'blue'};
`;
<Box variant="red">...</Box>

// ✅ $ prefiksi ishlatish
const Box = styled.div`
  color: ${p => p.$variant === 'red' ? 'red' : 'blue'};
`;
<Box $variant="red">...</Box>

Render ichida styled komponent yaratish

// ❌ Har render da yangi klass yaratiladi — juda sekin
function Component() {
  const Title = styled.h1` font-size: 24px; `; // ← bu yerda emas!
  return <Title>Salom</Title>;
}

// ✅ Komponentdan tashqarida bir marta yarating
const Title = styled.h1` font-size: 24px; `;

function Component() {
  return <Title>Salom</Title>;
}

Bu eng ko'p uchraydigan xato. Komponent har render qilganda yangi CSS klassi yaratiladi — sahifa sekinlashadi, DevTools da yuzlab klaslar to'planib ketadi.


Haddan tashqari nesting

// ❌ Yomon — hamma narsa bitta katta styled komponentda
const Card = styled.div`
  padding: 20px;

  h3 {
    font-size: 18px;
    span {
      color: red;
      strong { font-weight: 900; }
    }
  }
  p { color: gray; }
  button { background: blue; }
`;

// ✅ Yaxshi — har element o'z komponenti
const Card = styled.div` padding: 20px; `;
const CardTitle = styled.h3` font-size: 18px; `;
const CardText = styled.p` color: gray; `;

CSS Modules dan farqli — Styled Components da element nomlari JSX da ko'rinadi. Bu afzallikdan foydalaning.


Styled Components vs CSS Modules

Styled Components:
  + Props bilan dinamik stil — juda qulay
  + Tema tizimi (ThemeProvider) — dark mode oson
  + Komponent va stil bir joyda
  + Fayllar alohida emas
  - Runtime xarajat bor (kichik, lekin bor)
  - Paket hajmi: ~12KB gzip
  - Server Side Rendering — qo'shimcha sozlash kerak

CSS Modules:
  + Zero runtime — build vaqtida hal bo'ladi
  + Oddiy CSS — yangi sintaksis yo'q
  + Server Side Rendering — muammosiz
  - Dinamik stil — className yig'ish kerak
  - Tema tizimi — CSS variables bilan qo'lda

Kichik-o'rta loyihalarda ikkalasi ham yaxshi ishlaydi. Ko'p dinamik stil kerak bo'lsa — Styled Components. Maksimal performance va oddiylik kerak bo'lsa — CSS Modules.


Xulosa

Styled Components — CSS ni komponent ichida, props orqali dinamik boshqarish imkonini beradi. Klass nomlari avtomatik noyob bo'ladi, nom konflikti yo'q.

Eslab qoling:

  • styled.div, styled.button — HTML elementdan komponent yaratish
  • styled(Component)` — mavjud komponentni kengaytirish
  • ${p => p.$variant} — props bilan dinamik stil
  • $ prefiksi — props HTMLga o'tmasligi uchun
  • .attrs() — default atributlar belgilash
  • keyframes — animatsiya yaratish
  • ThemeProvider — global tema tizimi
  • Styled komponentni render tashqarisida e'lon qilish

Keyingi dars

37-dars: Tailwind CSS — React bilan sozlash, classNames kutubxonasi