N
HeadlessCoreHeadless Core
Back to Home

Magazine features — community, bookmarks, RTL, card media

HeadlessCore ships a magazine / news / blog community layer comparable to third-party headless magazine templates (login, registration, server-side bookmarks, frontend post submission, profile management) — without buying a separate product. Everything runs on WPGraphQL + JWT inside this repo.

Related guides: Getting Started (first-time setup), WordPress Setup (admin fields), GraphQL Reference (mutations), Troubleshooting (login failures).


Feature summary

Feature Logged out Logged in
Save / bookmark posts localStorage (/saved) User meta + /saved synced
Login / register /login Header shows account name
Profile edit /account
Frontend posting /account/posts (when enabled)
Blog card audio preview
Blog card video preview ✅ (YouTube inline)
Blog card gallery slider
View count badge
RTL layout (i18n locales)

Quick setup (5 minutes)

1. Required plugins

Activate via TGMPA when you enable the HeadlessCore theme:

  • WPGraphQL
  • WPGraphQL JWT Authentication

2. JWT secret in wp-config.php

Open your WordPress root wp-config.php (same folder as wp-admin/). Add above the line /* That's all, stop editing! Happy publishing. */:

/* Add any custom values between this line and the "stop editing" line. */

// Signs JWT auth tokens for Next.js login/register (magazine community layer).
define( 'GRAPHQL_JWT_AUTH_SECRET_KEY', 'paste-a-long-random-secret-here' );

Generate a secret: https://api.wordpress.org/secret-key/1.1/salt/ — copy one of the returned lines (quotes only, not the define part).

Do not commit wp-config.php to git — it contains database credentials and this secret.

3. Admin toggles

Headless Core → Settings → Magazine Community

Toggle Effect
Enable public registration /register works; syncs WordPress “Anyone can register”; registerUser GraphQL allowed
Enable frontend posting /account/posts CRUD; contributors submit pending posts for review

Click Save Changes.

4. Test on Next.js

No new Next.js env vars are required for community auth — tokens are stored in the browser (localStorage) after login.

cd nextjs-starter-kit
npm run dev
URL What to test
http://localhost:3000/register Create account (only when registration enabled)
http://localhost:3000/login Sign in
http://localhost:3000/account Edit name, email, bio
http://localhost:3000/account/posts/new Submit a post (when posting enabled)
http://localhost:3000/blog Bookmark icon on cards
http://localhost:3000/saved Saved list (synced when logged in)

Frontend routes

Path Auth Gated by
/login Public
/register Public enableRegistration (redirects to /login when off)
/account Required Redirects to /login
/account/posts Required enableFrontendPosting
/account/posts/new Required enableFrontendPosting
/account/posts/[id]/edit Required enableFrontendPosting + own post
/saved Public Uses account list when logged in

Source files:

  • nextjs-starter-kit/src/lib/authContext.tsx — session provider
  • nextjs-starter-kit/src/lib/communityClient.ts — GraphQL login/register/mutations
  • nextjs-starter-kit/src/components/CommunityAuthLinks.tsx — header links
  • nextjs-starter-kit/src/components/SaveButton.tsx — bookmark toggle

WordPress backend

File: wp-content/themes/wp-headless-theme/inc/community-features.php

GraphQL: themeSettings.community

{
  themeSettings {
    community {
      enableRegistration
      enableFrontendPosting
    }
  }
}

User fields

Field Type Description
savedPostIds [Int] Bookmarked post database IDs
communitySavedPosts [CommunitySavedPost] Title, slug, cover, date, category for /saved

Mutations (JWT required except register/login)

Mutation Purpose
login JWT plugin — returns authToken, refreshToken, user
registerUser WPGraphQL — blocked when registration toggle off
updateUser Profile (used by /account)
toggleSavedPost Bookmark / unbookmark one post
syncSavedPosts Replace full bookmark list
submitCommunityPost Create post (pending unless user can publish_posts)
updateCommunityPost Edit own post (or any if edit_others_posts)
deleteCommunityPost Delete own post

Example bookmark toggle:

mutation ToggleSaved($postId: Int!) {
  toggleSavedPost(input: { postId: $postId }) {
    saved
    savedPostIds
  }
}

Example submit post:

mutation Submit($title: String!, $content: String!) {
  submitCommunityPost(input: { title: $title, content: $content }) {
    result {
      success
      message
      post { databaseId slug status title }
    }
  }
}

Send the JWT on every authenticated request:

Authorization: Bearer <authToken>

Roles & moderation

  • New users registered while frontend posting is enabled get the Contributor role (can write but not publish).
  • submitCommunityPost creates pending posts for contributors; editors publish from wp-admin Posts.
  • Users with publish_posts get published posts immediately.

Save / bookmark posts

Logged out: src/lib/savedPosts.ts stores a snapshot in localStorage (key hc_saved_posts) so /saved works without an account.

Logged in: toggleSavedPost writes to user meta (headlesscore_saved_posts). SaveButton uses server state; /saved loads communitySavedPosts from GraphQL.


RTL (right-to-left)

Scope: Base infrastructure + header/nav/breadcrumbs/blog-card arrows — not a full audit of all 52 pro blocks.

  • src/lib/rtl.tsRTL_LOCALES (ar, he, fa, ur, …), getTextDirection()
  • src/app/layout.tsx sets <html dir="rtl|ltr"> from active locale (i18n)
  • .rtl-flip in globals.css for arrow icons that do not auto-mirror

View counts on blog cards

  • viewCount on WPGraphQL Post (inc/analytics.php) reads existing page-view analytics for /blog/{slug}.
  • Badge on BlogList.tsx and single post page (hidden when count is 0).

Audio / video / gallery on blog cards

src/lib/mediaPreview.ts scans post content HTML for pro block markers:

Block Card behavior
Audio Player (hc-audio-player__el) Play/pause on card
Video Embed (hc-video-embed) YouTube: thumbnail + play → inline iframe
Image Gallery (hc-image-gallery__img) On-card image slider

Implemented in src/components/BlogCardMedia.tsx.


Production deployment

Apache (XAMPP)

JWT auth needs the Authorization header passed to PHP. In .htaccess or Apache config:

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Nginx

See WPGraphQL JWT Authentication README — typically fastcgi_pass with authorization header forwarding.

Checklist

  • GRAPHQL_JWT_AUTH_SECRET_KEY set in production wp-config.php (unique per environment)
  • Magazine Community toggles configured in wp-admin
  • NEXT_PUBLIC_WORDPRESS_API_URL points to production GraphQL endpoint
  • HTTPS on both WordPress and Next.js
  • Test /register, /login, bookmark, and post submit after deploy

Comparison with external magazine templates

Capability HeadlessCore (this repo) Typical Faust-based magazine theme
Self-hosted MIT core Often commercial license
Faust.js dependency ❌ (direct WPGraphQL)
WooCommerce + corporate modes ✅ same kit Usually blog-only
Server bookmarks
Frontend posting ✅ (HTML editor) ✅ (often richer editor)
Card media previews

You do not need a second headless product for magazine features — enable them here.


Troubleshooting

See Troubleshooting — Magazine community.

Common issues:

Problem Fix
Login returns GraphQL error JWT plugin active; GRAPHQL_JWT_AUTH_SECRET_KEY in wp-config.php
Register disabled Turn on Enable public registration in settings
Post submit forbidden Turn on Enable frontend posting; user needs edit_posts
401 on mutations Pass Authorization: Bearer header with valid token
Apache login always fails Add HTTP_AUTHORIZATION rewrite (see above)

Phase history

  • Phase 31 — RTL, localStorage bookmarks, view counts, audio/video badges, WooCommerce cart fix
  • Phase 32 — Full community layer (auth, server bookmarks, frontend posting, gallery/video card upgrades)

See ROADMAP.md for details.