Overview
Thalyson’s Portfolio uses a centralized content.json file located at src/utils/content.json to manage all text content, navigation, project data, and metadata. This approach provides:
Single source of truth for all portfolio content
Bilingual support (Portuguese BR and English)
Easy content updates without touching component code
Type-safe content when consumed by React components
The content management system separates content from presentation logic, making it easy to update portfolio information without modifying React components.
Content Structure
The content.json file is organized into logical sections that map to portfolio components:
{
"header" : { /* Navigation links */ },
"hero" : { /* Landing section */ },
"about" : { /* About me section */ },
"projects" : { /* Projects metadata */ },
"projectsData" : [ /* Project entries */ ],
"vexiunData" : { /* Featured project */ },
"technicalDecisions" : { /* Architecture highlights */ },
"contact" : { /* Contact information */ },
"footer" : { /* Footer links */ },
"metadata" : { /* SEO metadata */ }
}
Bilingual Content
All user-facing text supports both Portuguese (BR) and English:
Hero Example
Component Usage
{
"hero" : {
"role" : {
"ptBR" : "Full-stack Developer • Product Builder" ,
"en" : "Full-stack Developer • Product Builder"
},
"hello" : { "ptBR" : "Olá, eu sou" , "en" : "Hi, I'm" },
"name" : "Thalyson Rafael" ,
"tagline" : {
"ptBR" : "Transformo visão técnica em produtos escaláveis e experiências de alto impacto." ,
"en" : "I turn technical vision into scalable products and high-impact experiences."
}
}
}
The translate() helper function automatically selects the correct language version based on the current language state managed by Zustand.
Key Content Sections
Defines the main navigation menu items:
{
"header" : {
"ariaLabel" : { "ptBR" : "Navegação principal" , "en" : "Main navigation" },
"nav" : [
{ "id" : "home" , "label" : { "ptBR" : "Início" , "en" : "Home" } },
{ "id" : "projects" , "label" : { "ptBR" : "Projetos" , "en" : "Projects" } },
{ "id" : "about" , "label" : { "ptBR" : "Sobre" , "en" : "About" } },
{ "id" : "contact" , "label" : { "ptBR" : "Contato" , "en" : "Contact" } }
]
}
}
Usage: Navigation links use the id for scroll anchors and label for display text.
Hero Section
Contains landing page content including role, tagline, features, and CTA buttons:
{
"hero" : {
"role" : { "ptBR" : "..." , "en" : "..." },
"hello" : { "ptBR" : "Olá, eu sou" , "en" : "Hi, I'm" },
"name" : "Thalyson Rafael" ,
"features" : [
{
"id" : "end-to-end" ,
"label" : { "ptBR" : "Do planejamento ao deploy" , "en" : "From planning to deployment" }
}
],
"actions" : {
"portfolio" : {
"label" : { "ptBR" : "Ver portfólio" , "en" : "View portfolio" },
"ariaLabel" : { "ptBR" : "Ver meus projetos em destaque" , "en" : "View my featured projects" }
}
}
}
}
Key fields:
features: Array of key value propositions displayed as badges
actions: CTA buttons with labels and ARIA labels for accessibility
About Section
Defines personal bio, tech stack, differentiation points, work process, and metrics:
{
"about" : {
"title" : { "ptBR" : "Sobre mim" , "en" : "About me" },
"intro" : {
"text1" : { "ptBR" : "..." , "en" : "..." },
"text2" : { "ptBR" : "..." , "en" : "..." }
},
"techStack" : [
{ "title" : "TypeScript" },
{ "title" : "Next.js" },
{ "title" : "Nest.js" }
],
"differentiation" : {
"title" : { "ptBR" : "O que me diferencia:" , "en" : "What sets me apart:" },
"items" : [
{
"title" : { "ptBR" : "Performance:" , "en" : "Performance:" },
"description" : { "ptBR" : "..." , "en" : "..." }
}
]
},
"metrics" : [
{
"value" : "5+" ,
"label" : { "ptBR" : "Projetos end-to-end" , "en" : "End-to-end projects" }
}
]
}
}
The techStack array only includes title (no bilingual object) because tech names are universal. Icons are mapped separately in the component via getIconTech().
Projects Data
Each project entry includes metadata, tech stack, links, and image gallery:
{
"projectsData" : [
{
"id" : "02" ,
"title" : "Equilibrium Center" ,
"projectType" : "Fullstack" ,
"category" : { "ptBR" : "Gestão / Saúde" , "en" : "Management / Health" },
"description" : {
"ptBR" : "Plataforma completa de gestão para massoterapeutas..." ,
"en" : "A complete management platform for massage therapists..."
},
"tech" : [
"Next.js" ,
"TypeScript" ,
"shadcn" ,
"PostgreSQL (Neon)" ,
"Stripe"
],
"links" : {
"github" : "https://github.com/ThalysonRibeiro/equilibrium-center" ,
"githubBackend" : "" ,
"app" : "" ,
"live" : "https://equilibrium-center.vercel.app"
},
"images" : [
{ "title" : "Equilibrium-Center-photo-1" , "image" : "/eq-center/1.webp" }
]
}
]
}
Required fields:
id: Unique identifier (string)
title: Project name
description: Bilingual project description
tech: Array of technology names
links: Object with github, githubBackend, app, and live URLs (use empty string if not applicable)
images: Array of image objects with title (for alt text) and image (public path)
Static contact details and social links:
{
"contact" : {
"static" : {
"email" : "[email protected] " ,
"phone" : "+55 65 98127-8291" ,
"location" : "Natal, Rio Grande do Norte, Brasil" ,
"socialLinks" : [
{
"id" : "linkedin" ,
"href" : "https://www.linkedin.com/in/thalyson-rafael-br" ,
"icon" : "FaLinkedinIn" ,
"color" : "hover:text-blue-400"
}
]
},
"i18n" : {
"title" : { "ptBR" : "Entre em contato" , "en" : "Get in touch" }
}
}
}
Icon mapping: The icon field references React Icons library. Icons are resolved dynamically in components.
Defines Open Graph and Twitter metadata for sharing:
{
"metadata" : {
"siteName" : { "ptBR" : "Thalyson Rafael" , "en" : "Thalyson Rafael" },
"title" : {
"ptBR" : "Thalyson Rafael | Full-stack Developer & Product Builder" ,
"en" : "Thalyson Rafael | Full-stack Developer & Product Builder"
},
"description" : {
"ptBR" : "Full-stack com Next.js, NestJS e TypeScript. Construo aplicações escaláveis..." ,
"en" : "Full-stack with Next.js, NestJS, and TypeScript. I build scalable apps..."
}
}
}
Usage: Consumed in src/app/layout.tsx to generate dynamic metadata for SEO.
Adding New Content
Add a New Project
Add project entry to projectsData array
Copy an existing project object and modify all fields: {
"projectsData" : [
{
"id" : "06" ,
"title" : "My New Project" ,
"projectType" : "Fullstack" ,
"category" : { "ptBR" : "SaaS" , "en" : "SaaS" },
"description" : {
"ptBR" : "Descrição do projeto em português" ,
"en" : "Project description in English"
},
"tech" : [ "Next.js" , "Prisma" , "PostgreSQL" ],
"links" : {
"github" : "https://github.com/username/repo" ,
"githubBackend" : "" ,
"app" : "" ,
"live" : "https://myproject.vercel.app"
},
"images" : [
{ "title" : "Screenshot 1" , "image" : "/my-project/1.webp" }
]
}
]
}
Add project images to public folder
Create a folder public/my-project/ and add your screenshots: public/
my-project/
1.webp
2.webp
3.webp
Verify the project appears
The project will automatically render in the Projects section without any code changes.
Add a New Field to Hero Section
Add the field to content.json
{
"hero" : {
"subtitle" : {
"ptBR" : "Novo subtítulo" ,
"en" : "New subtitle"
}
}
}
Update the Hero component
import content from "@/utils/content.json" ;
import { translate } from "@/utils/i18n" ;
export function Hero () {
const { lang } = useLanguageStore ();
return (
< div >
< h1 > { translate ( content . hero . hello , lang ) } </ h1 >
< h2 > { translate ( content . hero . subtitle , lang ) } </ h2 >
</ div >
);
}
Add a New Navigation Link
Add nav item to header.nav array
{
"header" : {
"nav" : [
{ "id" : "home" , "label" : { "ptBR" : "Início" , "en" : "Home" } },
{ "id" : "blog" , "label" : { "ptBR" : "Blog" , "en" : "Blog" } }
]
}
}
Create corresponding section with matching ID
< section id = "blog" >
< h2 > Blog </ h2 >
</ section >
The header component automatically maps nav items to smooth-scroll anchors using the id field.
Content Validation
Type Safety
While content.json is a plain JSON file, you can create a TypeScript interface for validation:
export interface BilingualText {
ptBR : string ;
en : string ;
}
export interface Project {
id : string ;
title : string ;
projectType : string ;
category : BilingualText ;
description : BilingualText ;
tech : string [];
links : {
github : string ;
githubBackend : string ;
app : string ;
live : string ;
};
images : Array <{ title : string ; image : string }>;
}
Validation Checklist
Before deploying content changes:
✅ Pre-deployment Checklist
Best Practices
Never hardcode text in components. Always pull from content.json for consistency and maintainability.
DO ✅
// Good: Content from JSON
const { lang } = useLanguageStore ();
< h1 > { translate ( content . hero . title , lang ) } </ h1 >
DON’T ❌
// Bad: Hardcoded text
< h1 > Welcome to my portfolio </ h1 >
Keep Content Organized
Group related content under logical keys:
{
"hero" : {
"title" : {},
"subtitle" : {},
"actions" : {}
}
}
Use Descriptive Keys
Prefer semantic keys over generic ones:
// Good
{ "actions" : { "portfolio" : { "label" : "View Portfolio" } } }
// Bad
{ "buttons" : { "btn1" : { "text" : "View Portfolio" } } }
Maintain Consistent Translation Quality
Use professional tone in both languages
Keep translations equivalent in meaning (not literal)
Maintain consistent terminology across sections
Translation Helpers
The portfolio uses a simple translation utility:
export type Lang = "ptBR" | "en" ;
export function translate (
text : { ptBR : string ; en : string },
lang : Lang
) : string {
return text [ lang ];
}
Usage:
import { translate } from "@/utils/i18n" ;
import { useLanguageStore } from "@/store/language-store" ;
const { lang } = useLanguageStore ();
const greeting = translate ( content . hero . hello , lang );
Next Steps
Styling Components Learn how to customize colors, fonts, and design tokens
Animation System Explore Framer Motion configuration and animation patterns