Ayush Verma Portfolio (This Website)
Production-grade portfolio with an AI assistant + SEO hardening

Role
Creator / Full-Stack Engineer
Period
2025
Category
full stack
Overview
A Next.js portfolio with an in-site assistant (rule-based + optional FastAPI RAG backend), navigation-safe UX, and production-grade SEO/social/crawl hardening.
Key Highlights
- Fully glassmorphic UI with standardized brand, layout, and component cards
- AI assistant with navigation discovery (teaches + offers buttons, no surprise redirects)
- Robust animations including tickers, orbits, and scroll-triggered reveals
- SEO + structured data designed for reliable rich results
Tech Stack
What I Learned
Case study: one JSON-LD "graph injector" (rich results without duplicate contexts)
Problem: Next.js makes it easy to add schema.org JSON-LD per page, but it’s also easy to accidentally emit multiple "@context" blocks (or emit nested @context inside nested entities). That can create noisy pages, flaky validators, and inconsistent rich-result parsing.
Solution: I built a tiny "JSON-LD graph injector" component that:
- Accepts either a single schema object or an array.
- Strips any nested "@context" from individual nodes.
- Emits one canonical "@context" and a single "@graph" payload.
This keeps the head clean, keeps schema composable, and avoids subtle structured-data issues when composing Organization + WebSite + WebPage + Breadcrumbs + FAQ on the same route.
type JsonLdProps = {
data: Record<string, unknown> | Array<Record<string, unknown>>;
};
export function JsonLd({ data }: JsonLdProps) {
const payload = Array.isArray(data) ? data : [data];
const graphNodes = payload.map((node) => {
const { "@context": _ctx, ...rest } = node as Record<string, unknown>;
return rest;
});
const json =
graphNodes.length === 1
? { "@context": "https://schema.org", ...graphNodes[0] }
: { "@context": "https://schema.org", "@graph": graphNodes };
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(json) }}
/>
);
}
Why it matters: it’s a small piece of code, but it prevents a whole class of "it validates on one page but not another" SEO issues — and it scales cleanly as the site grows.