Skip to content

Builder hub blog#18146

Merged
wackerow merged 28 commits into
devfrom
builder-hub-blog
May 28, 2026
Merged

Builder hub blog#18146
wackerow merged 28 commits into
devfrom
builder-hub-blog

Conversation

@mnelsonBT
Copy link
Copy Markdown
Contributor

@mnelsonBT mnelsonBT commented May 7, 2026

Launches a new blog section under /developers/blog/ for the Builder Growth team (and others) to publish long-form technical content.

Description

Blog infrastructure

  • BlogLayout — new layout template for individual blog posts, with table of contents, breadcrumbs, reading time, and file contributors. Mirrors the established TutorialLayout patterns.
  • BlogPostMetadata — metadata strip component showing author, team, tags, published date, and reading time.
  • /developers/blog/ listing page — server-rendered listing sorted by publish date (newest first), with per-post tags, team attribution, and reading time.
  • getBlogPostsData utility — uses the shared getContentListData helper to load posts from blogPosts.json slug registry. Adding a new post only requires creating a content directory and appending its slug to the JSON array.
  • BlogFrontmatter interface — extends SharedFrontmatter with author, published, team, tags, sourceUrl, image, breadcrumb, and hideEditButton.

Blog posts

Post Author Published Type
Building on Ethereum in 2026 Philip Krause 2026-05-07 Original
Privacy apps on Ethereum Philip Krause 2026-04-14 Syndicated from X
Agentic commerce infrastructure Rick 2026-03-27 Syndicated from rick.build

JSON-LD structured data

  • Author personas (3 new entries in KNOWN_PERSONS)
  • Listing page (page-jsonld.tsx) — CollectionPage schema with ItemList of all posts, full breadcrumb trail, and contributor list.
  • Individual posts (page-jsonld.tsx in [...slug]) — enhanced to emit BlogPosting type for blog slugs, with author resolution via KNOWN_PERSONS alias map.

Developer hub integration

  • "Recent updates" carousel on /developers/ page linking to latest blog posts.
  • Blog listed in breadcrumb navigation as "Builder updates" via custom i18n mapping.

Notes

sourceUrl without source

The TutorialFrontmatter has both source (display label) and sourceUrl (link). We added only sourceUrl to BlogFrontmatter because the display label can be derived from the URL if we ever build the component, and we are not building it now.

blogPosts.json slug registry

New posts are added by creating a public/content/developers/blog/<slug>/index.md directory and appending the slug to src/data/blogPosts.json. This mirrors the internalTutorials.json pattern and keeps the content data layer simple

Preview link

https://deploy-preview-18146.ethereum.it/latest

@netlify
Copy link
Copy Markdown

netlify Bot commented May 7, 2026

Deploy Preview for ethereumorg ready!

Name Link
🔨 Latest commit fed5762
🔍 Latest deploy log https://app.netlify.com/projects/ethereumorg/deploys/6a183feda700060008970e51
😎 Deploy Preview https://deploy-preview-18146.ethereum.it
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
7 paths audited
Performance: 70 (🟢 up 5 from production)
Accessibility: 96 (no change from production)
Best Practices: 100 (no change from production)
SEO: 98 (🔴 down 1 from production)
PWA: 59 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added content 🖋️ This involves copy additions or edits tooling 🔧 Changes related to tooling of the project translation 🌍 This is related to our Translation Program labels May 7, 2026
@mnelsonBT mnelsonBT marked this pull request as draft May 7, 2026 19:39
pettinarip and others added 2 commits May 11, 2026 16:58
Hub/index routes (`/[locale]/developers`, `/[locale]/developers/blog`,
`/[locale]/developers/tutorials`, `/[locale]/videos`) read frontmatter
from `public/content/` at render time and may be opted into ISR via
revalidating data getters (events, video thumbnails, etc.). The global
`outputFileTracingExcludes` rule for `public/content` then leaves those
reads empty inside the Netlify serverless function on ISR re-render --
listings silently degrade to "no posts" / empty grids until the next
deploy.

Add `outputFileTracingIncludes` to re-pull the MD subset each hub reads
(English + all locale translations). Per-route includes win over the
global exclude. Drafted to test the function-bundle delta and ISR
behavior on a Netlify preview before merging.
fix(netlify): include hub-route MD in the function bundle
@github-actions github-actions Bot added the config ⚙️ Changes to configuration files label May 11, 2026
@mnelsonBT mnelsonBT marked this pull request as ready for review May 12, 2026 13:21
mnelsonBT added 2 commits May 12, 2026 14:10
…into builder-hub-blog

# Conflicts:
#	app/[locale]/[...slug]/page-jsonld.tsx
@mnelsonBT mnelsonBT marked this pull request as draft May 15, 2026 12:00
@mnelsonBT mnelsonBT marked this pull request as ready for review May 27, 2026 19:40
@mnelsonBT
Copy link
Copy Markdown
Contributor Author

@wackerow changes in May 27 commits:

  • URL structure moved from /developers/blog/ to /latest/
  • Updated /latest/ landing page hero image
  • Added hero images to post frontmatter
  • Refactored BlogLayout to use MdxHero (instead of a custom hero image config)
  • Updated /developers/ carousel fallback to use the new post header images
  • Deleted unused images and older post files

myelinated-wackerow and others added 3 commits May 27, 2026 12:45
- patch: TagsInlineText to accept undefined array elements, filter out, and guard against empty
- refactor: use TagsInlineText
- refactor: Card subcomponent usage
- fix(intl): use formatDate

Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
myelinated-wackerow and others added 6 commits May 28, 2026 01:21
- revert blog layout addition
- update tutorial layout to accomodate blog post features

Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
@myelinated-wackerow
Copy link
Copy Markdown
Collaborator

Mirror external blog post hero images to S3

Out of scope for the current PR. This comment is placeholder context; intent is to convert it into a follow-up issue via GitHub's "Reference in new issue" once this PR merges.

Context

Blog posts under /latest/ support an optional image: field in frontmatter that renders on the article page and preview cards. Today all posts use local /images/... paths, but contributors will want to reference external URLs (third-party hosting, generated artwork, screenshots from external tools). Next.js will refuse to render those via next/image unless the source domain is allowlisted in remotePatterns — and we don't want to open the allowlist post by post. The canonical pattern in this repo is to fetch external images at sync time and mirror them to S3 (s3-dcl1.ethquokkaops.io), which is already allowlisted.

Proposed approach

Follow the existing data-layer Trigger.dev pattern (see src/data-layer/fetchers/fetchEvents.ts and fetchApps.ts for the logo/banner upload pattern; fetchVideoThumbnails.ts for a per-item conditional upload). The skill that codifies this lives at .claude/skills/data-layer.

Wiring checklist:

  • src/data-layer/fetchers/fetchBlogImages.ts — new fetcher. Reads public/content/latest/*/index.md via the GitHub API (fetchers can't access the app filesystem; see fetchGitHubContributors.ts for the pattern). Parses frontmatter, applies isExternal() from @/lib/utils/url, calls uploadToS3(url, "latest/posts") from @/data-layer/s3 for each external image. Returns Record<slug, string> mapping slug → mirrored S3 URL.
  • src/data-layer/tasks.ts — add KEYS.LATEST_IMAGES = "fetch-latest-images" and a DAILY task tuple (or HOURLY if cadence warrants).
  • src/data-layer/index.ts — getter passthrough: export const getLatestImages = () => get<Record<string, string>>(KEYS.LATEST_IMAGES).
  • src/lib/data/index.ts — cached wrapper via createCachedGetter with appropriate revalidation window.
  • src/data-layer/mocks/fetch-latest-images.json — mock for local dev.
  • src/lib/utils/md.ts getBlogPostsData — read the cached mapping; if frontmatter.image is external and the slug has an entry, substitute the S3 URL before returning. If not external, pass through unchanged.

uploadToS3 already handles SSRF protection, content-type validation, dedupe via SHA-256 of the source URL, 5MB size cap, and existence checks against S3. Nothing new to build there.

Open design question — fallback when mapping is absent

Worth verifying when this is picked up: Trigger.dev tasks in this repo appear to fire on production deploy. If correct, the timing concern largely takes care of itself — a PR merging a new external-image post deploys with its mapping populated as part of the same deploy cycle. What remains is choosing a fallback for the edge cases: first-ever build before any mapping exists, trigger task failure, or a slug missing from the mapping for any reason. Reasonable default: render the deterministic tile fallback (already the path for posts without an image: field). Pass-through of an unmirrored external URL is a non-starter — that would still require per-source remotePatterns allowlisting.

Acceptance criteria

  • Blog post with external image: in frontmatter renders the mirrored S3 URL on /latest/<slug> and in the /developers/ recent-posts carousel
  • isExternal() check gates the upload — local paths pass through untouched
  • Build does not fail when a mapping entry is missing for a known slug
  • Mock file populated so local dev works without S3 creds
  • No new entries added to remotePatterns in next.config.js (the whole point)

Reference points already in place

  • src/data-layer/s3.tsuploadToS3 and uploadManyToS3
  • src/lib/utils/url.ts:17isExternal(href)
  • next.config.js already allowlists s3-dcl1.ethquokkaops.io
  • Authoritative skill reference: .claude/skills/data-layer

myelinated-wackerow and others added 7 commits May 28, 2026 03:36
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Serves an RSS 2.0 feed at /latest/feed.xml, locale-aware via the existing [locale] route segment. Channel title and description pull from the page-developers-blog translation namespace so feeds for translated content get translated channel metadata for free. Items include author (via dc:creator), publish date, description, and per-tag categories.

Adds a <link rel="alternate" type="application/rss+xml"> auto-discovery tag on the /latest/ page head.

Extends the proxy matcher to opt feed paths back into next-intl handling -- the file-extension exclusion in the primary matcher would otherwise bypass locale routing for /latest/feed.xml and short-circuit the canonical redirect for /en/latest/feed.xml.

Narrows the vestigial feed.xml entry in .gitignore to /feed.xml so the new route directory isn't silently ignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
- " | ethereum.org" suffix automatically applied

Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
- Migrate from page-developers-blog namespace usage
- Fixes ICU variable usage for "{minutes} min read"

Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Copy link
Copy Markdown
Member

@wackerow wackerow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mnelsonBT Looks good! Patched a handful of things and added the RSS feed. Should be good to go! Nice job 🔥

@wackerow wackerow merged commit 55b457a into dev May 28, 2026
13 of 14 checks passed
@wackerow wackerow deleted the builder-hub-blog branch May 28, 2026 13:32
@pettinarip pettinarip mentioned this pull request May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config ⚙️ Changes to configuration files content 🖋️ This involves copy additions or edits tooling 🔧 Changes related to tooling of the project translation 🌍 This is related to our Translation Program

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants