Skip to content

Add New/Updated recency pills to docs sidebar#959

Merged
tannerlinsley merged 3 commits into
mainfrom
docs-sidebar-recency-pills
Jun 4, 2026
Merged

Add New/Updated recency pills to docs sidebar#959
tannerlinsley merged 3 commits into
mainfrom
docs-sidebar-recency-pills

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented Jun 4, 2026

What

Adds "New" (green) and "Updated" (blue) recency pills to the docs sidebar, so readers can spot fresh content at a glance.

Each sidebar entry can carry optional addedAt / updatedAt ISO dates in a library's docs/config.json:

{ "label": "Installation", "to": "framework/react/installation", "addedAt": "2026-06-01" }
  • addedAt within the last 7 days → New pill
  • updatedAt within the last 7 days → Updated pill ("New" wins if both are recent)
  • Pills auto-expire after 7 days, so the sidebar self-cleans with no follow-up edits

This is maintainer-controlled and costs nothing at request time (no per-page GitHub API calls). The dates live in each library repo's docs/config.json; this PR makes tanstack.com support and render them.

Why

Requested by a user on X (reply to @tan_stack): "can we have new badge on sidebar?? like for new features??"

Changes

  • src/utils/config.ts — optional addedAt / updatedAt on sidebar children (valibot schema for core + per-framework entries, and the MenuItem type)
  • src/components/DocsLayout.tsx — pure getDocRecency() helper (7-day window; guards invalid and future dates) and a DocRecencyPill component, rendered in both the external <a> and internal <Link> sidebar branches
  • tanstack-docs-config.schema.json — the two new fields added so maintainers get editor validation/autocomplete

Verification

  • pnpm run test (tsc + ESLint): clean (0 errors)
  • Manual browser test against local Query docs: New + Updated render correctly in light and dark mode, entries without dates show nothing, and a date older than 7 days correctly shows no pill (auto-expiry)

Follow-up

To make pills visible on a given library, maintainers add addedAt / updatedAt to entries in that repo's docs/config.json.

Summary by CodeRabbit

  • New Features
    • Documentation sidebar now shows "New" or "Updated" recency badges for pages added or modified within the last 7 days; dates are parsed as local time to avoid timezone day shifts.
    • The docs config/schema accepts optional addedAt and updatedAt date fields so pages can surface these recency badges; invalid or future dates are ignored.

AlemTuzlak and others added 2 commits June 4, 2026 11:30
Doc pages can now surface a New or Updated pill in the docs sidebar,
driven by optional `addedAt` / `updatedAt` ISO dates in each library's
`docs/config.json`. Pills auto-expire after 7 days, so the sidebar
self-cleans with no follow-up edits and no per-page GitHub API calls.

- config.ts: add optional addedAt/updatedAt to the sidebar child valibot
  schema (core + per-framework) and the MenuItem type
- DocsLayout.tsx: add pure getDocRecency() helper (7-day window, guards
  invalid and future dates) and DocRecencyPill; render in both the
  external <a> and internal <Link> branches; New takes priority over
  Updated when both are recent
- tanstack-docs-config.schema.json: add the two fields so maintainers get
  editor validation/autocomplete
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5e2a3431-af27-456c-9d47-b4b215069049

📥 Commits

Reviewing files that changed from the base of the PR and between 4f1f104 and 27b6027.

📒 Files selected for processing (1)
  • src/components/DocsLayout.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/DocsLayout.tsx

📝 Walkthrough

Walkthrough

This pull request adds visual "New" and "Updated" indicators to sidebar documentation menu items. The feature reads optional addedAt and updatedAt timestamps from the docs config, displays recency badges within a 7-day window, and handles both external and internal navigation links in the sidebar rendering.

Changes

Sidebar Recency Pills

Layer / File(s) Summary
Config contract and validation
src/utils/config.ts, tanstack-docs-config.schema.json
MenuItem children type adds optional addedAt and updatedAt ISO date strings; configSchema valibot validation and JSON schema both accept and validate these new date fields for both framework-agnostic and framework-specific sidebar children.
Recency logic and pill component
src/components/DocsLayout.tsx
A 7-day RECENCY_WINDOW_MS constant, DocRecency type union, getDocRecency() function that classifies docs as "new" or "updated" while validating timestamps, and DocRecencyPill React component that renders styled badges with optional tooltip dates parsed as local time.
Sidebar menu rendering integration
src/components/DocsLayout.tsx
During sidebar menu construction, recencyPill elements are computed per child item using the recency logic and rendered inline with both external (<a>) and internal (<Link>) menu item labels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

I nibble code and hop with cheer,
New pages gleam for seven days clear,
A tiny pill that shows what's fresh,
No timezone tricks to make it mesh,
Hooray — the sidebar wears its sash! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding recency pills (New/Updated indicators) to the documentation sidebar.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs-sidebar-recency-pills

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/DocsLayout.tsx`:
- Around line 39-46: getDocRecency and DocRecencyPill parse date-only strings
inconsistently (UTC vs local), causing the recency pill to disagree with the
tooltip across timezones; create a shared parser (e.g., parseDateOnlyIso or
parseDocDate) that detects "YYYY-MM-DD" and constructs a local Date (new
Date(year, month-1, day)) and use that parser in isRecent (or getDocRecency) and
in DocRecencyPill when formatting the tooltip so both recency gating and
displayed date are computed from the same normalized Date instance.

In `@src/utils/config.ts`:
- Around line 33-34: Replace the loose v.optional(v.string()) validators for
addedAt/updatedAt with a shared recencyDateField using Valibot’s v.isoDate
wrapped in v.optional to enforce YYYY-MM-DD; update the validators in the
top-level fields and the nested frameworks[].children[] entries (the same
properties referenced in parseDocsConfig / the config schema) to use
recencyDateField so safeParse only accepts ISO date strings and preserves the
“New/Updated” recency logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 178ce36e-e3ee-42b9-bb5f-5eb08f8046d4

📥 Commits

Reviewing files that changed from the base of the PR and between 4363ad0 and 4f1f104.

📒 Files selected for processing (3)
  • src/components/DocsLayout.tsx
  • src/utils/config.ts
  • tanstack-docs-config.schema.json

Comment on lines +39 to +46
const isRecent = (iso?: string) => {
if (!iso) return false
const time = new Date(iso).getTime()
if (Number.isNaN(time)) return false
const age = now - time
// Reject future dates; only flag within the window.
return age >= 0 && age <= RECENCY_WINDOW_MS
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In JavaScript, does new Date('YYYY-MM-DD') parse as UTC or local time, and what timezone edge cases does that create for day-based windows like “last 7 days”?

💡 Result:

In JavaScript, the new Date('YYYY-MM-DD') constructor parses date-only strings as UTC midnight [1][2][3]. This is a specific behavior mandated by the ECMAScript specification for date-only strings, which differs from ISO 8601's general recommendation to treat such strings as local time [2][4][5]. Conversely, date-time strings (such as 'YYYY-MM-DDTHH:mm:ss') are parsed as local time [2][6][3]. This discrepancy creates significant edge cases for day-based windows like "last 7 days": 1. Off-by-one Date Shifts: Because new Date('YYYY-MM-DD') creates a UTC timestamp, it will effectively represent a different calendar day for users in timezones west of UTC [2][7]. For example, a user in New York (UTC-5) parsing '2026-06-04' will get a Date object representing '2026-06-03' at 7:00 PM local time [2]. When the application performs comparisons against "today," this shifted timestamp may cause the system to misidentify the current day [7]. 2. DST and Calculation Errors: If you calculate date ranges by dividing milliseconds (e.g., diff / 86400000), you may encounter errors when the window crosses a Daylight Saving Time (DST) boundary [8]. In these cases, a "day" may not consist of exactly 24 hours, leading to non-integer results or incorrect counts [8]. 3. Inconsistent Comparison Context: Comparing a UTC-parsed date object against local-time-based "current" time often leads to silent bugs [7][9]. If your application logic mixes UTC-parsed inputs with local-time-calculated windows, the "last 7 days" window will drift based on the user's specific timezone and their system's local clock [10][9]. To avoid these issues, developers are encouraged to: - Avoid new Date(string) for parsing whenever possible [2][4]. - Use explicit component-based construction, such as new Date(year, monthIndex, day), which correctly uses the local timezone [11][2]. - Normalize dates to a "UTC day index" (e.g., Date.UTC(y, m, d) / 86400000) for comparisons to ensure they remain consistent regardless of the user's timezone or DST status [8]. - Rely on user-profile-specific timezones instead of the host machine's local time for application-wide date filtering [10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the targeted file and functions/components
ls -la src/components/DocsLayout.tsx

rg -n "getDocRecency|DocRecencyPill" src/components/DocsLayout.tsx

# Show the relevant code around the indicated lines and around the located functions
python3 - <<'PY'
import itertools, pathlib
path = pathlib.Path("src/components/DocsLayout.tsx")
lines = path.read_text().splitlines()
for start,end in [(1,120),(30,110),(35,95),(1,80)]:
    print("\n### slice",start,"-",end)
    for i in range(start-1,min(end,len(lines))):
        print(f"{i+1:4d}: {lines[i]}")
PY

Repository: TanStack/tanstack.com

Length of output: 13914


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show code around the call site where DocRecencyPill is rendered
python3 - <<'PY'
import pathlib
path = pathlib.Path("src/components/DocsLayout.tsx")
lines = path.read_text().splitlines()
start, end = 700, 780
for i in range(start, min(end, len(lines))):
    print(f"{i+1:4d}: {lines[i]}")
PY

# Find where addedAt/updatedAt are defined/typed/constructed
rg -n "addedAt|updatedAt" src | head -n 80

# Also search for config.json references mentioning recency fields
rg -n "config\.json|docs/config" -S src | head -n 80

# Identify the type of child (where getDocRecency(child.addedAt, child.updatedAt) comes from)
rg -n "getDocRecency\(" src/components/DocsLayout.tsx

Repository: TanStack/tanstack.com

Length of output: 10332


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "addedAt" src/utils src/components | head -n 200
rg -n "updatedAt" src/utils/config* src/utils | head -n 200

# Inspect config schema definitions around where MenuItem type is defined
rg -n "type MenuItem|interface MenuItem|addedAt\\??:" src/utils/config.ts src/utils -S || true

# Print relevant sections of src/utils/config.ts (small targeted windows)
python3 - <<'PY'
import pathlib, re
p = pathlib.Path("src/utils/config.ts")
if not p.exists():
    print("missing src/utils/config.ts")
    raise SystemExit(0)
lines = p.read_text().splitlines()
# print windows around likely occurrences
for pat in ["addedAt", "updatedAt", "MenuItem", "docs/config", "recency", "type ConfigSchema"]:
    for i,l in enumerate(lines):
        if pat in l:
            start=max(0,i-20); end=min(len(lines), i+40)
            print(f"\n=== around line {i+1} (pattern {pat}) ===")
            for j in range(start,end):
                print(f"{j+1:4d}: {lines[j]}")
            break
PY

Repository: TanStack/tanstack.com

Length of output: 18490


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "dateOnly|YYYY-MM-DD|\\(\\\\d\\{4\\}\\)-\\(\\\\d\\{2\\}\\)-\\(\\\\d\\{2\\}\\)|toLocaleDateString\\(" src | head -n 200
rg -n "parseDocDate|getDocRecency|RECENCY_WINDOW" src/components src/utils | head -n 200

Repository: TanStack/tanstack.com

Length of output: 3719


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check file sizes quickly
wc -l src/utils/dates.ts

# Print relevant part of dates.ts
nl -ba src/utils/dates.ts | sed -n '1,220p'

Repository: TanStack/tanstack.com

Length of output: 149


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n src/utils/dates.ts

Repository: TanStack/tanstack.com

Length of output: 3749


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find likely config.json files in-repo (avoid build dirs)
fd -e json "config.json" . | head -n 200

# Search for addedAt/updatedAt fields in JSON files (prioritize config.json)
rg -n "\"addedAt\"|\"updatedAt\"" --glob='**/*.{json,ts,tsx,js,jsx,mjs,cjs}' . | head -n 120

Repository: TanStack/tanstack.com

Length of output: 383


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print the relevant sections around the schema lines
python3 - <<'PY'
import pathlib
p = pathlib.Path("tanstack-docs-config.schema.json")
lines = p.read_text().splitlines()
for start,end in [(40,110),(80,120)]:
    print(f"\n### slice {start}-{end}")
    for i in range(start-1, min(end, len(lines))):
        print(f"{i+1:4d}: {lines[i]}")
PY

Repository: TanStack/tanstack.com

Length of output: 5082


Deduplicate date parsing for addedAt/updatedAt to keep recency gating and tooltip aligned

getDocRecency parses addedAt/updatedAt via new Date(iso) (date-only strings become UTC midnight), while DocRecencyPill parses YYYY-MM-DD as local time before using toLocaleDateString(). Since the docs config schema defines these as date-only (e.g. "2026-06-01"), the “last 7 days” pill can shift relative to the tooltip day across timezones.

💡 Suggested fix
+const DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/
+
+function parseDocDate(value?: string): Date | null {
+  if (!value) return null
+  const m = DATE_ONLY_RE.exec(value)
+  const parsed = m
+    ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]))
+    : new Date(value)
+  return Number.isNaN(parsed.getTime()) ? null : parsed
+}
+
 function getDocRecency(addedAt?: string, updatedAt?: string): DocRecency {
   const now = Date.now()
 
   const isRecent = (iso?: string) => {
-    if (!iso) return false
-    const time = new Date(iso).getTime()
-    if (Number.isNaN(time)) return false
+    const parsed = parseDocDate(iso)
+    if (!parsed) return false
+    const time = parsed.getTime()
     const age = now - time
     const age = now - time
     return age >= 0 && age <= RECENCY_WINDOW_MS
   }
@@
   let title: string | undefined
   if (date) {
-    const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date)
-    const parsed = dateOnly
-      ? new Date(
-          Number(dateOnly[1]),
-          Number(dateOnly[2]) - 1,
-          Number(dateOnly[3]),
-        )
-      : new Date(date)
-    if (!Number.isNaN(parsed.getTime())) {
+    const parsed = parseDocDate(date)
+    if (parsed) {
       title = `${isNew ? 'Added' : 'Updated'} ${parsed.toLocaleDateString()}`
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/DocsLayout.tsx` around lines 39 - 46, getDocRecency and
DocRecencyPill parse date-only strings inconsistently (UTC vs local), causing
the recency pill to disagree with the tooltip across timezones; create a shared
parser (e.g., parseDateOnlyIso or parseDocDate) that detects "YYYY-MM-DD" and
constructs a local Date (new Date(year, month-1, day)) and use that parser in
isRecent (or getDocRecency) and in DocRecencyPill when formatting the tooltip so
both recency gating and displayed date are computed from the same normalized
Date instance.

Comment thread src/utils/config.ts
Comment on lines +33 to +34
addedAt: v.optional(v.string()),
updatedAt: v.optional(v.string()),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For valibot v1.3.1, what is the recommended validator for optional YYYY-MM-DD date strings (and is there a built-in helper preferred over regex)?

💡 Result:

For Valibot v1.3.1, the recommended validator for a YYYY-MM-DD date string is the built-in isoDate action [1][2]. This is preferred over using a custom regex, as it is a standard, maintained, and type-safe solution provided by the library [2][3]. You can implement this by piping v.string with v.isoDate [1]. To make the field optional, use the v.optional wrapper [4][5]. Example implementation: import * as v from 'valibot'; const OptionalDateSchema = v.optional( v.pipe( v.string, v.isoDate('Invalid date format, expected YYYY-MM-DD.'))); Please note that as documented by Valibot, the built-in isoDate validator uses a regex that checks for the YYYY-MM-DD format but does not validate the maximum number of days based on the specific month or leap years (e.g., "2023-06-31" would be considered valid) [1][2]. If strict calendar date validation is required, you may need to add an additional custom validation step [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate config schema file
SCHEMA_FILE="$(fd -i 'tanstack-docs-config\.schema\.json' . || true)"
echo "SCHEMA_FILE=$SCHEMA_FILE"

# Inspect the current validation code around the reported lines
echo "---- src/utils/config.ts (lines 1-120) ----"
if [ -f "src/utils/config.ts" ]; then
  sed -n '1,120p' src/utils/config.ts | nl -ba | sed -n '1,120p'
else
  echo "Missing src/utils/config.ts"
fi

# Search for addedAt/updatedAt in config utils
echo "---- usages of addedAt/updatedAt in src/utils/config.ts ----"
rg -n "addedAt|updatedAt" src/utils/config.ts || true

# Inspect schema json if found
if [ -n "${SCHEMA_FILE:-}" ] && [ -f "$SCHEMA_FILE" ]; then
  echo "---- schema: occurrences of addedAt/updatedAt ----"
  rg -n "\"addedAt\"|\"updatedAt\"|addedAt|updatedAt" "$SCHEMA_FILE" || true

  echo "---- schema: surrounding context (first few matches) ----"
  rg -n "\"addedAt\"|\"updatedAt\"" "$SCHEMA_FILE" -n | head -n 20 || true
else
  echo "No tanstack-docs-config.schema.json found via fd"
fi

# Find where configSchema is used/validated (optional but helps justify impact)
echo "---- search for configSchema usage ----"
rg -n "configSchema" src || true

Repository: TanStack/tanstack.com

Length of output: 218


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SCHEMA_FILE="$(fd -i 'tanstack-docs-config\.schema\.json' . || true)"
echo "SCHEMA_FILE=${SCHEMA_FILE:-<not found>}"

echo "---- src/utils/config.ts (lines 1-120) ----"
if [ -f "src/utils/config.ts" ]; then
  python3 - <<'PY'
import pathlib
p = pathlib.Path("src/utils/config.ts")
lines = p.read_text(encoding="utf-8").splitlines()
for i in range(1, min(120, len(lines))+1):
    print(f"{i:4d}: {lines[i-1]}")
PY
fi

echo "---- reported snippet (lines 25-60) ----"
if [ -f "src/utils/config.ts" ]; then
  python3 - <<'PY'
import pathlib
p = pathlib.Path("src/utils/config.ts")
lines = p.read_text(encoding="utf-8").splitlines()
start, end = 25, 60
for i in range(start, min(end, len(lines))+1):
    print(f"{i:4d}: {lines[i-1]}")
PY
fi

echo "---- usages of addedAt/updatedAt in src/utils/config.ts ----"
if [ -f "src/utils/config.ts" ]; then
  rg -n "addedAt|updatedAt" src/utils/config.ts || true
fi

if [ -n "${SCHEMA_FILE:-}" ] && [ -f "$SCHEMA_FILE" ]; then
  echo "---- schema: occurrences of addedAt/updatedAt ----"
  rg -n "\"addedAt\"|\"updatedAt\"|addedAt|updatedAt" "$SCHEMA_FILE" || true

  echo "---- schema: surrounding context for those keys (first ~80 lines around first match) ----"
  # grab first match line number
  first=$(rg -n "\"addedAt\"|\"updatedAt\"" "$SCHEMA_FILE" | head -n 1 | cut -d: -f1 || true)
  if [ -n "${first:-}" ]; then
    start=$((first-40)); if [ $start -lt 1 ]; then start=1; fi
    end=$((first+40))
    python3 - <<PY
import pathlib
p = pathlib.Path("$SCHEMA_FILE")
lines = p.read_text(encoding="utf-8").splitlines()
start=$start; end=$end
for i in range(start, min(end, len(lines))+1):
    print(f"{i:5d}: {lines[i-1]}")
PY
  fi
else
  echo "Schema file not found; skipping schema inspection."
fi

echo "---- search for schema validation usage (configSchema/config) ----"
rg -n "configSchema|tanstack-docs-config\.schema|validate|parse" src || true

Repository: TanStack/tanstack.com

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for recency/new/updated rendering logic and usages of addedAt/updatedAt
rg -n "addedAt|updatedAt" src | head -n 200
echo "----"
rg -n "\"New\"|New\" pill|Updated\"|recency|sidebar pill|sidebar.*pill" src | head -n 200
echo "----"
rg -n "format\\s*===\\s*['\"]date['\"]|format.*date|isoDate|YYYY-MM-DD" src/utils src/components src/routes | head -n 200

Repository: TanStack/tanstack.com

Length of output: 16463


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect recency rendering/parsing logic
python3 - <<'PY'
import pathlib
p = pathlib.Path("src/components/DocsLayout.tsx")
lines = p.read_text(encoding="utf-8").splitlines()
for i in range(1, 120):
    print(f"{i:5d}: {lines[i-1]}")
PY

echo "---- recency usage near sidebar pill ----"
python3 - <<'PY'
import pathlib
p = pathlib.Path("src/components/DocsLayout.tsx")
lines = p.read_text(encoding="utf-8").splitlines()
start, end = 700, 820
for i in range(start, min(end, len(lines))+1):
    print(f"{i:5d}: {lines[i-1]}")
PY

# Search for valibot isoDate usage in repo
rg -n "isoDate\\b" src || true

# Print the exact section of schema json around addedAt/updatedAt matches
SCHEMA_FILE="./tanstack-docs-config.schema.json"
python3 - <<'PY'
import pathlib, re
p = pathlib.Path("./tanstack-docs-config.schema.json")
lines = p.read_text(encoding="utf-8").splitlines()
# find first occurrences with line numbers
targets = ["\"addedAt\"", "\"updatedAt\""]
for t in targets:
    for idx, line in enumerate(lines, start=1):
        if t in line:
            start=max(1, idx-8); end=min(len(lines), idx+20)
            print(f"\n---- {t} around line {idx} ({start}-{end}) ----")
            for j in range(start, end+1):
                print(f"{j:5d}: {lines[j-1]}")
            break
PY

Repository: TanStack/tanstack.com

Length of output: 12630


Tighten addedAt/updatedAt validation to YYYY-MM-DD to preserve recency pills

src/utils/config.ts currently uses v.optional(v.string()) for addedAt/updatedAt (also at the nested frameworks[].children[] occurrence). This lets arbitrary strings pass parseDocsConfig’s v.safeParse, but src/components/DocsLayout.tsx treats non-parseable dates (new Date(iso)NaN) as “not recent”, so the “New/Updated” sidebar pill is skipped silently. The published contract (tanstack-docs-config.schema.json) declares these fields as type: string with format: date (example "2026-06-01"), so runtime validation should match.

Use Valibot’s built-in v.isoDate (prefer it over a custom regex) wrapped in v.optional(...), e.g. via a shared recencyDateField and wire it to both addedAt and updatedAt.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/config.ts` around lines 33 - 34, Replace the loose
v.optional(v.string()) validators for addedAt/updatedAt with a shared
recencyDateField using Valibot’s v.isoDate wrapped in v.optional to enforce
YYYY-MM-DD; update the validators in the top-level fields and the nested
frameworks[].children[] entries (the same properties referenced in
parseDocsConfig / the config schema) to use recencyDateField so safeParse only
accepts ISO date strings and preserves the “New/Updated” recency logic.

@tannerlinsley tannerlinsley merged commit 1fc07b1 into main Jun 4, 2026
9 checks passed
@tannerlinsley tannerlinsley deleted the docs-sidebar-recency-pills branch June 4, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants