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.

Konsultacje

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.

Konsultacje

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.

Konsultacje

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
  • Audyt SEO i Performance
  • Testy automatyczne i QA
  • Konsultacje Produktowe
  • 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
Next.jsReactWordPress

REST API WordPressa — integracja z React i Next.js

WordPress REST API z Next.js — autentykacja, custom endpointy i cache bez typowych pułapek, w które wpada każdy, kto robi to pierwszy raz.

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

WordPress to nie tylko PHP i szablony, ponieważ od wersji 4.7 ma wbudowane REST API, dzięki któremu może funkcjonować jako headless CMS. Backend w WordPressie, frontend w React lub Next.js to w wielu projektach bardzo rozsądne rozwiązanie.

Artykuł w skrócie

  • REST API jest wbudowane od WordPressa 4.7 — pod warunkiem włączonych ładnych permalinków; sprawdzisz 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.

W tym artykule pokażę jak prosto zbudować tę integrację od podstaw, a działa tu zarówno 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/., jak i architektura CMS, czyli Content Management System, to system do zarządzania treścią bez ręcznej edycji kodu.. Nie wiesz jeszcze za dużo o WordPressie? Zobacz artykuł WordPressa od zera — instalacji, konfiguracji i podstaw.

Czym jest WordPress REST API?

REST API to interfejs HTTP do danych WordPressa, który zamiast renderować gotowy HTML, WordPress zwraca czysty 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 dostajesz surowe dane, po które dowolny frontend w JavaScript może sięgnąć i wyrenderować je po swojemu.

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, bo 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ą, więc wystarczy jego obecność (?_embed), a 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, by dopuszczać wyłącznie zaufane domeny (np. własny frontend) i nigdy nie pozwalać „każdemu” (*) na zapytania z ciasteczkami logowania. Te dwie zasady 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

Wbudowane endpointy często nie wystarczają i wtedy warto napisać własne:

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

Przy prostych projektach REST API w zupełności wystarczy, a po WPGraphQL warto sięgnąć dopiero, gdy zapytania robią się złożone.

Werdykt Labu

WordPress REST API to solidny fundament pod architekturę headless CMS. Wbudowane endpointy rozwiązują od razu większość typowych problemów z pobieraniem danych, a jeśli potrzebujesz specyficznej logiki biznesowej, możesz wykorzystać własne endpointy. Autentykację dopasujesz do projektu, od prostych Application Passwords po tokeny JWT, a parametry _embed i _fields pozwalają dokładnie kontrolować wielkość paczek JSON i dbać o wydajność. W połączeniu z App Routerem i mechanizmem ISR Next.js tworzy z WordPressem wartościową kombinację.

Osiągnięcie właściwego balansu daje redaktorom swobodę w korzystaniu ze znajomego panelu do zarządzania treścią, a Ty masz pełną kontrolę nad wydajnością i nowoczesnym frontendem. Więcej o tym, kiedy taki wariant się opłaca, znajdziesz w artykule Headless WordPress + Next.js — kiedy warto?.

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.
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.

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
Headless WordPress z Next.js — kiedy ma sens, a kiedy nie
Headless WordPress z Next.js — kiedy ma sens, a kiedy nie

Headless WordPress z Next.js: zalety, koszty, proces redakcyjny, podgląd szkiców, SEO i sytuacje, w których klasyczny WordPress wygrywa.

Maciej Sala

Maciej Sala

Founder Strivelab

26 lutego 2025
REST API — zasady projektowania i dobre praktyki
REST API — zasady projektowania i dobre praktyki

REST API zaprojektowane naprędce wróci do Ciebie z długiem. Konwencje, wersjonowanie i obsługa błędów — zasady, których tutoriale zwykle pomijają.

Maciej Sala

Maciej Sala

Founder Strivelab

5 grudnia 2025
WordPress: instalacja i podstawy — kompletny przewodnik
WordPress: instalacja i podstawy — kompletny przewodnik

WordPress od instalacji po deployment — hooki, REST API, bezpieczeństwo i workflow aktualizacji. Bez pomijania rzeczy, które bolą na produkcji.

Maciej Sala

Maciej Sala

Founder Strivelab

17 lutego 2026
Następny wpisHeadless WordPress z Next.js — kiedy ma sens, a kiedy nieHeadless WordPress z Next.js: zalety, koszty, proces redakcyjny, podgląd szkiców, SEO i sytuacje, w których klasyczny WordPress wygrywa.
Maciej Sala

Maciej Sala

Founder Strivelab

26 lutego 2025