StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Doradztwo produktowe

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Doradztwo produktowe

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Doradztwo produktowe

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

RealizacjeO mnieBlog
Porozmawiajmy
PL
EN

Nowoczesne strony internetowe dla firm, które myślą odważnie.

Przewiń do góry

Nazwa

StriveLab Maciej Sala

NIP

6772218995

REGON

524008527

E-mail

contact@strivelab.pl

Usługi główne
  • Tworzenie stron internetowych
  • Strony internetowe Next.js
  • Strony internetowe Astro
  • Strony internetowe React
Inne usługi
  • Usługi
  • SEO & Performance Sprint
  • QA & Stabilizacja
  • Konsultacje Product / Delivery
  • Automatyzacja Procesów AI
  • Aplikacje webowe Next.js
  • Współpraca ciągła
Strony
  • O mnie
  • Usługi
  • Realizacje
  • Blog

© 2026 StriveLab.pl

Polityka prywatności
WordPressNext.jsReact

REST API WordPressa — integracja z React i Next.js

Jak połączyć WordPress z nowoczesnym frontendem bez typowych pułapek? REST API, autentykacja, custom endpoints, cache i praktyczne przykłady z Next.js.

OpublikujLinkedInFacebookWyślij
Autor
Maciej Sala
Opublikowano
29 stycznia 2025 09:00
Czytanie
5 min czytania
Aktualizacja
20 maja 2026 09:01

Artykuł w skrócie

  • REST API jest wbudowane od WordPressa 4.7 — pod warunkiem włączonych ładnych permalinków; sprawdzasz je, otwierając /wp-json/wp/v2/posts.
  • _embed i _fields kontrolują wielkość odpowiedzi — pierwszy dołącza powiązane dane (obrazek, kategorie) w jednym requeście, drugi tnie zbędne pola i transfer.
  • Paginacja siedzi w nagłówkach — X-WP-Total i X-WP-TotalPages, a nie w samym JSON-ie, który zwraca tylko bieżącą stronę.
  • CORS tylko dla zaufanych domen — nigdy gwiazdka * z Access-Control-Allow-Credentials: true; to wprost chroni przed kradzieżą sesji.
  • Zapis wymaga autentykacji server-to-server — Application Passwords (od WP 5.6) po HTTPS; nigdy nie wysyłaj poświadczeń do klienta w przeglądarce.
  • Cachuj na dwóch poziomach — Transient API po stronie WordPressa dla kosztownych zapytań i revalidate po stronie Next.js dla ISR.

Od razu powinieneś wiedzieć, że WordPress to nie tylko PHP i szablony, ponieważ od wersji 4.7 ma wbudowane REST API to interfejs udostępniający dane przez standardowe metody HTTP (GET, POST...) pod adresami zasobów — w WordPressie domyślnie pod /wp-json/., dzięki któremu może funkcjonować jako headless CMS, czyli Content Management System, to system do zarządzania treścią bez ręcznej edycji kodu.. Backend w WordPressie, podczas gdy frontend istnieje w React lub Next.js to w wielu projektach sensowne rozwiązanie.

W tym artykule pokażę jak prosto zbudować tę integrację od podstaw, a jeśli nie wiesz jeszcze za dużo o WordPressie to zacznij od WordPressa od zera — instalacji, konfiguracji i podstaw.

Czym jest WordPress REST API?

REST API to interfejs HTTP do danych WordPressa, który zamiast renderować HTML, WordPress zwraca JSON:

Code
# Pobierz posty
curl https://twojadomena.pl/wp-json/wp/v2/posts
 
# Pobierz strony
curl https://twojadomena.pl/wp-json/wp/v2/pages
 
# Pobierz media
curl https://twojadomena.pl/wp-json/wp/v2/media

W odpowiedzi otrzymujemy czyste dane, które są idealne dla frontendu działającego na JavaScript.

Wbudowane endpointy

WordPress udostępnia domyślnie:

EndpointMetodyOpis
/wp/v2/postsGET, POST, PUT, DELETEPosty
/wp/v2/pagesGET, POST, PUT, DELETEStrony
/wp/v2/mediaGET, POST, PUT, DELETEMedia
/wp/v2/categoriesGET, POST, PUT, DELETEKategorie
/wp/v2/tagsGET, POST, PUT, DELETETagi
/wp/v2/usersGET, POST, PUT, DELETEUżytkownicy
/wp/v2/commentsGET, POST, PUT, DELETEKomentarze

Przy listingach zwróć uwagę na nagłówki odpowiedzi X-WP-Total i X-WP-TotalPages — to z nich budujesz paginację po stronie klienta. Sam JSON zwraca tylko bieżącą stronę.

Parametry zapytań

Code
# Paginacja
/wp/v2/posts?page=2&per_page=10
 
# Filtrowanie po kategorii
/wp/v2/posts?categories=5
 
# Wyszukiwanie
/wp/v2/posts?search=javascript
 
# Sortowanie
/wp/v2/posts?orderby=date&order=desc
 
# Wybór pól (oszczędność transferu)
/wp/v2/posts?_fields=id,title,slug,excerpt
 
# Embed (dołącz powiązane dane)
/wp/v2/posts?_embed

Parametr _embed jest flagą — wystarczy jego obecność (?_embed), wartość true jest ignorowana po stronie serwera, choć zadziała. W dalszych przykładach używam _embed=true dla czytelności w URLSearchParams.

Konfiguracja WordPress

1. Permalinki

REST API wymaga ładnych permalinków:

Code
Ustawienia → Bezpośrednie odnośniki → Nazwa wpisu (lub dowolne inne niż "Prosty")

2. CORS (Cross-Origin)

Jeśli frontend jest na innej domenie:

Code
// functions.php lub wtyczka
add_action('rest_api_init', function() {
    remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
    add_filter('rest_pre_serve_request', function($value) {
        $origin = get_http_origin();
        $allowed_origins = [
            'http://localhost:3000',
            'https://moj-frontend.vercel.app',
        ];
 
        if (in_array($origin, $allowed_origins)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
            header('Access-Control-Allow-Credentials: true');
            header('Access-Control-Allow-Headers: Authorization, Content-Type');
        }
 
        return $value;
    });
});

Najważniejsze: dopuszczaj tylko adresy domen, którym ufasz (np. własny frontend), i nigdy nie pozwalaj „każdemu” (*) na zapytania z ciasteczkami logowania — to dwie zasady, które realnie chronią przed kradzieżą sesji użytkowników.

3. Ukrycie wrażliwych danych

Nie zakładaj, że każde pole z odpowiedzi powinno być publiczne. Jeśli wystawiasz własne endpointy albo pola użytkowników, jawnie ograniczaj dane:

Code
add_filter('rest_prepare_user', function($response, $user, $request) {
    unset($response->data['email']);
    return $response;
}, 10, 3);

Integracja z Next.js

Klient API

Code
// lib/wordpress.ts
 
const WP_URL = process.env.WORDPRESS_URL || 'https://twojadomena.pl'
const API_URL = `${WP_URL}/wp-json/wp/v2`
 
interface WPPost {
  id: number
  slug: string
  title: { rendered: string }
  content: { rendered: string }
  excerpt: { rendered: string }
  date: string
  featured_media: number
  categories: number[]
  _embedded?: {
    'wp:featuredmedia'?: Array<{ source_url: string }>
    'wp:term'?: Array<Array<{ id: number; name: string; slug: string }>>
  }
}
 
interface FetchOptions {
  page?: number
  perPage?: number
  categories?: number[]
  search?: string
  slug?: string
}
 
export async function getPosts(options: FetchOptions = {}): Promise<WPPost[]> {
  const params = new URLSearchParams({
    _embed: 'true',
    per_page: String(options.perPage || 10),
    page: String(options.page || 1),
  })
 
  if (options.categories?.length) {
    params.set('categories', options.categories.join(','))
  }
 
  if (options.search) {
    params.set('search', options.search)
  }
 
  if (options.slug) {
    params.set('slug', options.slug)
  }
 
  const response = await fetch(`${API_URL}/posts?${params}`, {
    next: { revalidate: 60 }, // ISR: odświeżaj dane co 60 sekund
  })
 
  if (!response.ok) {
    throw new Error(`WordPress API error: ${response.status}`)
  }
 
  return response.json()
}
 
export async function getPostBySlug(slug: string): Promise<WPPost | null> {
  const posts = await getPosts({ slug })
  return posts[0] || null
}
 
export async function getCategories() {
  const response = await fetch(`${API_URL}/categories?per_page=100`, {
    next: { revalidate: 3600 },
  })
  return response.json()
}

Strona listingu

Code
// app/blog/page.tsx
 
import { getPosts } from '@/lib/wordpress'
import Link from 'next/link'
 
export const revalidate = 60
 
export default async function BlogPage() {
  const posts = await getPosts({ perPage: 12 })
 
  return (
    <main className="container mx-auto px-4 py-8">
      <h1 className="mb-8 text-3xl font-bold">Blog</h1>
 
      <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
        {posts.map((post) => {
          const featuredImage =
            post._embedded?.['wp:featuredmedia']?.[0]?.source_url
 
          return (
            <article
              key={post.id}
              className="overflow-hidden rounded-lg border"
            >
              {featuredImage && (
                <img
                  src={featuredImage}
                  alt=""
                  className="h-48 w-full object-cover"
                />
              )}
              <div className="p-4">
                <h2 className="mb-2 text-xl font-semibold">
                  <Link href={'/blog/' + post.slug}>
                    <span
                      dangerouslySetInnerHTML={{ __html: post.title.rendered }}
                    />
                  </Link>
                </h2>
                <div
                  className="line-clamp-3 text-gray-600"
                  dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
                />
              </div>
            </article>
          )
        })}
      </div>
    </main>
  )
}

Strona pojedynczego posta

Code
// app/blog/[slug]/page.tsx
 
import { getPostBySlug, getPosts } from '@/lib/wordpress'
import { notFound } from 'next/navigation'
 
interface Props {
  params: Promise<{ slug: string }>
}
 
// Generowanie statycznych ścieżek
export async function generateStaticParams() {
  const posts = await getPosts({ perPage: 100 })
  return posts.map((post) => ({ slug: post.slug }))
}
 
// Metadata dla SEO
export async function generateMetadata({ params }: Props) {
  const { slug } = await params
  const post = await getPostBySlug(slug)
 
  if (!post) {
    return { title: 'Nie znaleziono' }
  }
 
  return {
    title: post.title.rendered,
    description: post.excerpt.rendered.replace(/<[^>]*>/g, '').slice(0, 160),
  }
}
 
export default async function PostPage({ params }: Props) {
  const { slug } = await params
  const post = await getPostBySlug(slug)
 
  if (!post) {
    notFound()
  }
 
  const featuredImage = post._embedded?.['wp:featuredmedia']?.[0]?.source_url
 
  return (
    <article className="container mx-auto max-w-3xl px-4 py-8">
      {featuredImage && (
        <img
          src={featuredImage}
          alt=""
          className="mb-8 h-64 w-full rounded-lg object-cover"
        />
      )}
 
      <h1
        className="mb-4 text-4xl font-bold"
        dangerouslySetInnerHTML={{ __html: post.title.rendered }}
      />
 
      <time className="mb-8 block text-gray-500">
        {new Date(post.date).toLocaleDateString('pl-PL')}
      </time>
 
      <div
        className="prose prose-lg max-w-none"
        dangerouslySetInnerHTML={{ __html: post.content.rendered }}
      />
    </article>
  )
}

Custom endpoints

Często wbudowane endpointy często nie wystarczają i dlatego jest wygodna opcja stworzenia własnych, endpointów:

Prosty endpoint

Code
// functions.php lub wtyczka
 
add_action('rest_api_init', function() {
    register_rest_route('moja-api/v1', '/featured-posts', [
        'methods'  => 'GET',
        'callback' => 'get_featured_posts',
        'permission_callback' => '__return_true',
    ]);
});
 
function get_featured_posts() {
    $posts = get_posts([
        'meta_key'       => 'is_featured',
        'meta_value'     => '1',
        'posts_per_page' => 5,
    ]);
 
    return array_map(function($post) {
        return [
            'id'    => $post->ID,
            'title' => $post->post_title,
            'slug'  => $post->post_name,
            'image' => get_the_post_thumbnail_url($post->ID, 'large'),
        ];
    }, $posts);
}

Endpoint z parametrami

Code
register_rest_route('moja-api/v1', '/posts-by-author/(?P<author_id>\d+)', [
    'methods'  => 'GET',
    'callback' => function($request) {
        $author_id = $request['author_id'];
        $page = $request->get_param('page') ?: 1;
 
        $query = new WP_Query([
            'author'         => $author_id,
            'posts_per_page' => 10,
            'paged'          => $page,
        ]);
 
        return [
            'posts'       => array_map('format_post', $query->posts),
            'total'       => $query->found_posts,
            'total_pages' => $query->max_num_pages,
        ];
    },
    'args' => [
        'author_id' => [
            'required'          => true,
            'validate_callback' => function($param) {
                return is_numeric($param);
            },
        ],
        'page' => [
            'default'           => 1,
            'validate_callback' => function($param) {
                return is_numeric($param) && $param > 0;
            },
        ],
    ],
    'permission_callback' => '__return_true',
]);

Autentykacja

Jeśli zamierzasz dodawać, edytować lub usuwać dane (przy użyciu metod POST, PUT i DELETE), musisz się wcześniej uwierzytelnić.

Application Passwords (WordPress 5.6+)

Code
Użytkownicy → Twój profil → Application Passwords → Dodaj

Użycie:

Code
const credentials = Buffer.from(
  `username:xxxx xxxx xxxx xxxx xxxx xxxx`,
).toString('base64')
 
const response = await fetch(`${API_URL}/posts`, {
  method: 'POST',
  headers: {
    Authorization: `Basic ${credentials}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'Nowy post',
    content: 'Treść posta...',
    status: 'publish',
  }),
})

To rozwiązanie jest sensowne głównie dla połączeń pomiędzy serwerami (np. Twój backend Next.js rozmawia z WordPressem) albo dla zaplecza redakcyjnego. Nie wysyłaj takich danych uwierzytelniających do publicznego klienta w przeglądarce.

JWT (z wtyczką)

Zainstaluj wtyczkę "JWT Authentication for WP REST API":

Code
// Pobierz token
const tokenResponse = await fetch(`${WP_URL}/wp-json/jwt-auth/v1/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'admin',
    password: 'haslo',
  }),
})
 
const { token } = await tokenResponse.json()
 
// Użyj tokena
const response = await fetch(`${API_URL}/posts`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ title: 'Test', content: '...', status: 'draft' }),
})

JWT bywa wygodne, ale to dodatkowa warstwa i dodatkowa odpowiedzialność za przechowywanie tokena, odświeżanie go i bezpieczeństwo po stronie klienta.

Dodawanie pól do odpowiedzi

Custom fields (ACF lub natywne)

Code
// Dodaj pole do odpowiedzi API
add_action('rest_api_init', function() {
    register_rest_field('post', 'reading_time', [
        'get_callback' => function($post) {
            $content = get_post_field('post_content', $post['id']);
            preg_match_all('/\p{L}+/u', wp_strip_all_tags($content), $matches);
            $word_count = count($matches[0]);
            return ceil($word_count / 200);
        },
    ]);
 
    // Jeśli używasz ACF
    register_rest_field('post', 'custom_subtitle', [
        'get_callback' => function($post) {
            return get_field('subtitle', $post['id']);
        },
    ]);
});

Teraz w odpowiedzi API:

Code
{
  "id": 123,
  "title": { "rendered": "Mój post" },
  "reading_time": 5,
  "custom_subtitle": "Podtytuł z ACF"
}

Podgląd postów (preview)

Aby zobaczyć podgląd szkicu (draftu) przed publikacją, potrzebujesz autoryzacji:

Code
// lib/wordpress.ts
export async function getPreviewPost(id: number, token: string) {
  const response = await fetch(`${API_URL}/posts/${id}?status=draft`, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
    cache: 'no-store',
  })
 
  return response.json()
}
Code
// app/preview/[id]/page.tsx
import { getPreviewPost } from '@/lib/wordpress'
import { cookies } from 'next/headers'
 
export default async function PreviewPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const token = (await cookies()).get('wp_preview_token')?.value
 
  if (!token) {
    return <div>Brak autoryzacji</div>
  }
 
  const post = await getPreviewPost(Number(id), token)
 
  return (
    <div className="bg-yellow-100 p-4">
      <p className="font-bold">PREVIEW MODE</p>
      <article dangerouslySetInnerHTML={{ __html: post.content.rendered }} />
    </div>
  )
}

Wydajność

1. Wybieraj tylko potrzebne pola

Code
// Zamiast pobierać wszystko
const posts = await fetch(`${API_URL}/posts?_embed=true`)
 
// Wybierz tylko potrzebne
const posts = await fetch(`${API_URL}/posts?_fields=id,title,slug,excerpt`)

Jeśli łączysz _fields z _embed, pamiętaj, że możesz przypadkiem wyciąć sobie potrzebne _embedded albo _links.

2. Cache na poziomie Next.js

Code
// ISR
const posts = await fetch(url, {
  next: { revalidate: 60 },
})
 
// Statyczne (pobierane w trakcie budowania aplikacji)
const posts = await fetch(url, {
  cache: 'force-cache',
})
 
// Bez cache
const posts = await fetch(url, {
  cache: 'no-store',
})

3. Cache w WordPress

Code
// Transient API dla kosztownych operacji
function get_featured_posts_cached() {
    $cached = get_transient('featured_posts');
 
    if ($cached !== false) {
        return $cached;
    }
 
    $posts = expensive_query_here();
    set_transient('featured_posts', $posts, HOUR_IN_SECONDS);
 
    return $posts;
}

Porównanie: REST API vs GraphQL (WPGraphQL)

AspektREST APIWPGraphQL
InstalacjaWbudowaneWymaga wtyczki
Elastyczność zapytańOgraniczonaPełna
Pobieranie zbędnych pólTak (overfetching)Nie
Próg wejściaNiższyWyższy
Dostępne narzędziaStandardowe (fetch)Apollo, urql
CacheStandardowy HTTPWymaga konfiguracji

Dla prostych projektów REST API wystarczy, ale już dla bardziej złożonych projektów warto używać WPGraphQL.

Werdykt Labu

WordPress REST API to bardzo solidny fundament pod architekturę headless CMS. W praktyce praca z nim sprowadza się do kilku cennych korzyści:

  • Wbudowane endpointy rozwiązują większość typowych problemów z pobieraniem danych już na samym początku.
  • Custom endpoints dają pełną swobodę, gdy musisz obsłużyć bardziej specyficzną logikę biznesową.
  • Autentykację łatwo dopasujesz do wymagań projektu — od prostych Application Passwords, przez ciastka, aż po tokeny JWT.
  • Next.js z App Routerem i mechanizmem ISR tworzy z WordPressem bardzo zgrabny duet.
  • Parametry _embed i _fields pozwalają precyzyjnie kontrolować wielkość przesyłanych paczek JSON co ułatwia dbanie o wydajność.

Headless WordPress w parze z Next.js daje świetny balans, ponieważ redaktorzy mają do dyspozycji znajomy panel do zarządzania treścią, a Ty pełną kontrolę nad wydajnością i nowoczesnym frontendem. Więcej o tym, kiedy taki wariant się opłaca, w artykule Headless WordPress + Next.js — kiedy warto?.

Jeśli planujesz postawić headless WordPress z frontendem w Next.js i chcesz uniknąć typowych pułapek z CORS, autentykacją i cache, pomogę zaprojektować i wdrożyć tę integrację albo zobacz, jak realizuję projekty w Next.js.

  • Czym jest WordPress REST API?1 min
  • Wbudowane endpointy1 min
  • Konfiguracja WordPress1 min
  • Integracja z Next.js1 min
  • Custom endpoints1 min
  • Autentykacja1 min
  • Dodawanie pól do odpowiedzi1 min
  • Podgląd postów (preview)1 min
  • Wydajność1 min
  • Porównanie: REST API vs GraphQL (WPGraphQL)1 min
  • Werdykt Labu1 min

Często zadawane pytania

Źródła i dokumentacjaZweryfikowano: 19 maja 2026

Materiały wykorzystane do weryfikacji artykułu „REST API WordPressa — integracja z React i Next.js”:

WordPress REST API reference, WordPress register_rest_route(), WordPress Application Passwords, Next.js caching and revalidating, jak skonfigurować sklep WooCommerce, zasady projektowania REST API.

Maciej Sala

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwija własne projekty side-projecty.

Moje artykułyWięcej o mnie

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
WordPress: instalacja i podstawy — kompletny przewodnik
WordPress: instalacja i podstawy — kompletny przewodnik

WordPress od podstaw: instalacja lokalna i produkcyjna, wp-config.php, motywy, wtyczki, hooki, REST API, bezpieczeństwo, wydajność i workflow aktualizacji.

Maciej Sala

Maciej Sala

Founder Strivelab

17 lutego 2026
Next.js vs WordPress w 2026 — kiedy polecam jedno, a kiedy drugie
Next.js vs WordPress w 2026 — kiedy polecam jedno, a kiedy drugie

Next.js vs WordPress w 2026 — obiektywne porównanie dla firm, freelancerów i agencji. Wydajność, SEO, bezpieczeństwo, koszty, łatwość edycji — kiedy który wybrać i dlaczego.

Maciej Sala

Maciej Sala

Founder Strivelab

10 kwietnia 2026
REST API — zasady projektowania i dobre praktyki
REST API — zasady projektowania i dobre praktyki

Praktyczny przewodnik po projektowaniu REST API. Konwencje URL, metody HTTP, błędy, wersjonowanie, paginacja i kilka ważnych niuansów, które zwykle pomija się w prostych tutorialach.

Maciej Sala

Maciej Sala

Founder Strivelab

5 grudnia 2025
Następny wpisWooCommerce od zera — konfiguracja sklepu, płatności i wysyłki krok po krokuJak skonfigurować WooCommerce od zera: produkty, płatności, wysyłka, podatki, REST API, HPOS i checklista przed startem sprzedaży.
Maciej Sala

Maciej Sala

Founder Strivelab

6 marca 2025