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 providernextjs-starter-kit/src/lib/communityClient.ts— GraphQL login/register/mutationsnextjs-starter-kit/src/components/CommunityAuthLinks.tsx— header linksnextjs-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).
submitCommunityPostcreates pending posts for contributors; editors publish from wp-admin Posts.- Users with
publish_postsget 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.ts—RTL_LOCALES(ar, he, fa, ur, …),getTextDirection()src/app/layout.tsxsets<html dir="rtl|ltr">from active locale (i18n).rtl-flipinglobals.cssfor arrow icons that do not auto-mirror
View counts on blog cards
viewCounton WPGraphQLPost(inc/analytics.php) reads existing page-view analytics for/blog/{slug}.- Badge on
BlogList.tsxand 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_KEYset in productionwp-config.php(unique per environment) - Magazine Community toggles configured in wp-admin
-
NEXT_PUBLIC_WORDPRESS_API_URLpoints 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.