npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ranklynk/nextjs

v0.1.2

Published

Hydration-safe RankLynk SEO delivery for the Next.js App Router.

Readme

RankLynk for Next.js

Hydration-safe delivery of RankLynk runtime_page_config through Next.js metadata and React Server Components. The package changes delivery correctness; it does not promise ranking gains.

Support

  • Next.js App Router 14, 15, and 16
  • React and React DOM 18.2 through 19.x
  • Node.js 20.18.1 or newer

The package is server-only. Do not import it from a file containing 'use client'.

Install

npm install @ranklynk/nextjs
npx ranklynk-nextjs init --site-url https://example.com

The initializer creates exactly two files: a server helper at src/lib/ranklynk.ts (or lib/ranklynk.ts) and a public read-only status route at /api/ranklynk/status. It does not edit pages, environment files, or package.json, and it refuses to overwrite files it did not generate.

Set the domain-scoped key only in the server environment:

RANKLYNK_API_KEY=replace-with-your-domain-key

Route integration

Use the same pathname in generateMetadata and the page. Next memoizes the identical server fetch across metadata and Server Components during a render.

import type { Metadata } from 'next'
import { RankLynkSeo, RankLynkSlot } from '@ranklynk/nextjs/server'
import { ranklynk } from '@/lib/ranklynk'

const pathname = '/about'
const baseMetadata: Metadata = {
  title: 'About Example',
  description: 'The native page description.',
}

export async function generateMetadata(): Promise<Metadata> {
  return ranklynk.generateMetadata(pathname, baseMetadata)
}

export default async function AboutPage() {
  const page = await ranklynk.getPage(pathname)

  return (
    <main>
      <article>
        <h1>About Example</h1>
        <p>Page-owned content.</p>
        <RankLynkSlot config={page.config} field="faq_html" className="faq" />
      </article>

      <RankLynkSeo
        config={page.config}
        pageOwnedFields={['faq_html']}
        existingSchemaTypes={['Organization']}
      />
    </main>
  )
}

pageOwnedFields is the duplicate-output contract. Every declared field must be rendered once with RankLynkSlot; RankLynkSeo then omits that field from its fallback. Undeclared body fields render once in a neutral, dependency-free <details> fallback.

For dynamic routes in Next 15 and 16, await params before building the pathname:

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  return ranklynk.generateMetadata(`/posts/${slug}`)
}

Host-owned language and headings

lang belongs to the root <html> element, so the package returns safe props instead of trying to mutate the document after rendering:

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const htmlProps = await ranklynk.htmlProps('/', { lang: 'en' })
  return <html {...htmlProps}><body>{children}</body></html>
}

The helper accepts only a bounded BCP 47 language tag. Invalid managed values fall back to the valid host language.

Heading text and semantic fixes also stay inside the host React tree. Identify each heading by its original level and its one-indexed occurrence among headings of that level:

import { RankLynkHeadingSlot } from '@ranklynk/nextjs/server'

<RankLynkHeadingSlot config={page.config} originalLevel={2} occurrence={1}>
  Host-authored heading
</RankLynkHeadingSlot>

The component applies the first valid matching heading_text_updates value, an exact level_corrections rule, or demote_extra_h1 for H1 occurrences after the first. It emits source-level markers for deployed parity checks, bounds occurrences to 1–1,000 and replacement text to 300 characters, and never queries or mutates the DOM. Render each original level/occurrence pair once.

What is delivered

| Runtime field | Native delivery | |---|---| | Title, description, canonical, hreflang, robots, keywords, Open Graph, Twitter | generateMetadata helper | | JSON-LD | Deduplicated Server Component scripts; existingSchemaTypes suppresses host-owned schema | | FAQ, internal links, discovery, answer block, entity markup | Page-owned RankLynkSlot or one visually safe fallback | | Missing H1 | Explicit RankLynkH1Slot; never automatic because the package cannot safely inspect the host tree | | HTML language | ranklynk.htmlProps() spread onto a host-owned <html> element | | Heading text, extra-H1 demotion, level corrections | Explicit RankLynkHeadingSlot using original level + occurrence | | Heading-preservation CSS, preconnect, LCP preload | Sanitized/additive Server Component output | | Viewport | ranklynk.mergeViewport(pathname, base) for a route/layout that owns generateViewport | | Image DOM rewrites, redirects, response headers, variants, discovery hub document | Not injected generically; these require a host-owned element, routing layer, or edge-safe path |

The package never branches on user agent, cookies, or browser state. Humans and search/AI crawlers receive the same server component tree.

Delivery status

The generated /api/ranklynk/status route is intentionally public so RankLynk's deployed hydration harness can bind proof to the running bundle. It returns only:

{
  "deploymentSha": "40-character-git-sha-or-null",
  "deploymentId": "vercel-deployment-id-or-null",
  "packageVersion": "0.1.2",
  "nextVersion": "16.2.10",
  "reactVersion": "19.2.7",
  "revalidateSeconds": 300
}

It never returns the API key, runtime config, environment name, commit message, or customer content. The response is dynamically generated with revalidation disabled at the HTTP cache layer.

The deployment SHA is resolved in this order: VERCEL_GIT_COMMIT_SHA, RANKLYNK_DEPLOYMENT_SHA, then GITHUB_SHA. RankLynk accepts only a full 40-character hexadecimal Git SHA; partial or malformed values are reported as null. Set RANKLYNK_DEPLOYMENT_SHA in the server runtime when the hosting platform does not expose a Vercel or GitHub deployment SHA automatically.

Failure and cache behavior

  • Config is fetched during rendering, never through deferred after() work.
  • Requests use Next's Data Cache with a 300-second default revalidation interval.
  • Redirects from the config endpoint are rejected so the domain key cannot be forwarded to another origin.
  • Invalid, oversized, timed-out, or empty responses fail closed to the host page's native SEO.
  • Use onError to connect delivery failures to server-side observability; callback failures are isolated from page rendering.

Initializer safety

Preview the two-file change:

npx ranklynk-nextjs init --site-url https://example.com --dry-run

Refresh a previously generated helper after changing the site URL:

npx ranklynk-nextjs init --site-url https://www.example.com --force

--force still refuses to overwrite a file without the RankLynk generated marker.