- AI
- A
AI website generator on ChatGPT and Next.js 15: Creating SEO‑optimized pages from scratch
Imagine a design agency that creates not just beautiful layouts, but entire technological ecosystems. By investing once in the development of unique components and a brand identity, designers gain the ability to generate professional websites at a speed of 50+ projects per hour. In practice, this comes down to a simple cycle: you send a prompt to ChatGPT, receive a configuration file in response, upload it to the application, and with a single build command create ready‑made, styled pages. All of this is already set up in the starter template, including authentication and a multilingual AI chat.
This tutorial is the first part of a large journey, in which we will create an AI system for automatically generating web pages with React 19 and Next.js 15. Our goal is not just speed, but architectural elegance and perfect design consistency.
The plan is as follows:
Part 1 (You are here): Analyzing the basic architecture: "greedy" routes, transformer component, and typed configs for standard pages.
Part 2: Complicating the task: generating documentation pages and interactive tutorials.
Part 3: Adding intelligence: configuring AI for autonomous content generation.
Bonus: Practical case — deploying, monetizing, and scaling a real application.
This is a step-by-step guide to creating your own v0-level corporate analog with registrations and payments. A real business on generating beautiful applications with a revolutionary interface from A to Z, in the final fourth publication. To build the whole system, subscribe. It will only get more interesting from here.
As always, I provide the source code of the finished solution so that you can immediately start creating blogs, stores, landing pages, etc. https://github.com/aifa-agi/aifa
Project tech stack: React 19, Next.js 15, Vercel, Prisma, Stripe, AI SDK, Chat GPT
The tech stack combines stability and advanced capabilities: React 19 and Next.js 15 with App Router provide lightning-fast rendering and parallel routes, Vercel guarantees zero-downtime deployments, and Prisma and Neon offer type-safe data access. Stripe integrates instant payments, AI SDK with ChatGPT adds an intelligent layer, allowing AIFA Dev Starter to generate interfaces, content, and prototypes in just minutes, already including authorization and multilingual AI chat.
In practice, it boils down to a simple cycle: you send a prompt to ChatGPT, receive a configuration file in response, upload it to the app, and with a single build command, create ready, styled pages. All of this is already set up in the starter template, including authorization and multilingual AI chat.
Or use full automation just like in v0, but with a focus on large corporate integrations. (In the updated https://aifa.dev after the third publication)
Here’s an example:
Who benefits from an AI website generation system: studios, corporations, startups, freelancers
New format design studios: from mockup to mass site launch in minutes
Imagine a design agency that creates not just beautiful mockups, but entire technological ecosystems. Once invested in the development of unique components and brand style, designers can generate professional websites at a rate of 50+ projects per hour.
This is not science fiction — it's a new business model where creativity meets automation. And yes, monetization is already built-in via Stripe — I'll talk about this in the upcoming parts!
Corporate implementations: unified style and scalability for enterprise sites
The classic case is a QR menu for a restaurant chain. Once a professional dish card system is created, the business owner can offer a ready-made solution to hundreds of establishments. Restaurants only adapt the colors to their brand and fill the content with their voice—while the professional design and UX remain unchanged.
Scalability, uniformity, and speed of implementation are exactly what the corporate segment needs in 2025.
Transformer architecture of components: JSON → React without manual layout
The AIFA Dev technology is based on two critically important components:
1. Intelligent prompt engineering
Specially trained instructions for AI that can extract precise examples of sections from vector databases and adapt them for specific tasks.
2. Transformer component architecture
React components that "understand" configuration files and turn JSON descriptions into live, interactive pages.
External models (ChatGPT, Claude) are excellent at generating content, but unpacking components are the heart of the system, which we build ourselves.
AI-ready documentation: how to use text as a knowledge base for ChatGPT
Don't read this tutorial—use it as an AI assistant!
The main value of this extensive material lies not in sequential reading, but in its ability to become a personal knowledge base for your projects.
How it works:
Copy the entire text into ChatGPT
Ask specific questions: "How to implement...?", "Why do we need...?", "Show me the code for..."
Get precise answers with ready-made components
This is not documentation for reading—it’s an interactive guide for quickly solving tasks. Try this approach, and you'll understand why modern technical documentation should be AI-ready!
1. Introduction: purpose, benefits, and the method's place among v0-generation generators
This tutorial does not aim to create a solution that outperforms v0 in terms of generation speed. The main goal is to ensure uniformity and harmony in the design across all pages of the project.
Yes, creating each individual page might take slightly more time compared to universal generators. However, the overall project costs are significantly reduced thanks to the following benefits:
Unified style of all components—each new page automatically fits into the overall design system
Harmony throughout the project—no stylistic breaks between different sections of the site
Reduced revision costs—minimal need for subsequent styling and adaptation
Scalability of the solution—the more pages, the higher the effectiveness of the approach
Result: the project looks visually cohesive, professional, and requires minimal effort to maintain consistency when scaling.
The main tutorial covers the creation of standard pages using basic header and footer components.
However, in the project’s source code, you will find custom implementations for specialized types of pages:
Documentation pages with extended navigation
Tutorial pages with interactive elements
Working with complex components:
Note that some components on the main page have more complex logic:
State management
Animations and transitions
Interactive elements
Integration of advanced solutions:
To integrate such complex components, there is the option to add custom components instead of standard sections. This allows you to:
Maintain system flexibility
Implement unique functionality
Maintain a consistent style even for non-standard solutions
Practical task:
Studying the process of creating and integrating custom components remains your practical task for consolidating acquired knowledge.
1.1. Concept: uniform brand pages created by AI from templates
The modern development process requires speed, interface consistency, and the ability to quickly respond to business requirements. We offer an architecture in which standard, formalized descriptions of design sections (Hero, CTA, FAQ, Features, etc.) are stored in a structured AI knowledge base.
By uploading your current UI/UX standards for each section there, you allow artificial intelligence to act not only as a content generator but also as a guarantor of a consistent brand style at all levels of page creation.
1.2. Five stages: from uploading design templates to SEO-optimized build
Uploading design standards:
Templates and instructions for each section are added to the AI knowledge base—how to visually and structurally implement them within the unified style of your brand.Processing a text request:
A user, project manager, or programmer creates a task in the form of a text prompt—describing the desired page or its elements.AI interpretation and structuring:
The AI analyzes the request, generates semantic content, breaks it into logical sections, and selects suitable templates from the knowledge base.Page code generation:
Based on the selected templates and generated content, the system creates strict PageConfigs—declarative JSON/Type descriptions of future pages. They ensure adherence to all design standards and simplify downstream development.Automatic assembly and building:
The ready configurations are used for static page generation with render components, resulting in a complete, SEO-optimized page that meets your corporate standards visually and technically.
1.3. Resource savings and conversion growth: business benefits of AI generation
Fast rollout of new landing pages and product pages—just a prompt is enough for the AI to generate a page based on current templates.
Consistent brand style and quality—automatically extracted and maintained for every new page.
Minimizing manual work and enabling A/B testing—scaling page variants and changing content via AI without developer involvement in layout.
Preparedness for multilingual support—all texts and elements are stored centrally, easily localized and adapted.
Infrastructure flexibility—easily integrates into CI/CD pipelines, modern hosting and AI-driven processes.
1.4. How to continue: a roadmap of three articles and a bonus case
In this tutorial, we will go through step-by-step:
Architecture of existing components — a detailed breakdown of the PageHtmlTransformer structure, the wrapper system (Wrapper/FullScreenWrapper), section components, and their interaction to understand the internal mechanisms of operation.
System operation principles — studying the logic of section rendering, configuration typing, routing mechanisms, and the link between configuration files and React components. This is necessary so that developers can independently extend these representations.
Creating instructions for artificial intelligence — forming a structured description of currently available types for metadata, header section, footer section. Adding requirements for creating a JSX fragment for the body section with detailed specifications and constraints.
Generating configuration files — practical application of the knowledge gained to create config files that will be used by AI for automatic page generation in compliance with all architectural principles and design standards.
2. Project structure: folders, files, and Next.js 15 routing logic
Tree
app/@right/
├── public/
│ ├── (_routing)/
│ │ └── /
│ │ └── page.tsx # 1. Dynamic routing
│ └── (_service)/
│ ├── (_config)/
│ │ └── public-pages-config.ts # 6. Public pages configuration
│ └── (_libs)/
│ └── utils.ts # 7. Utilities
└── (_service)/
├── (_types)
│ ├── /page-transformer-custom-types/
│ └── page-wrapper-types.ts # 5. Types and interfaces
└── (_components)/
└── page-transformer-components/
├── page-html-transformer.tsx # 2. Main transformer
├── custom-sections/
├── wrappers/
│ ├── full-screen-wrapper.tsx # 3. Full-screen wrapper
│ └── wrapper.tsx # 4. Standard wrapper
├── header-sections-components/
│ ├── header-section.tsx # 8. Header component
│ ├── page-header-elements.tsx # 9. Header elements
│ └── announcement.tsx # 10. Announcement component
├── body-sections-components/
│ └── body-section.tsx # 12. Body component
└── footer-sections-components/
└── footer-section.tsx # 11. Footer component
Components:
1-@/app/@right/public/(_routing)//page.tsx
2-@/app/@right/(_service)/(_components)/page-transformer-components/page-html-transformer.tsx
3-@/app/@right/(_service)/(_components)/page-transformer-components/wrappers/full-screen-wrapper.tsx
4-app/@right/(_service)/(_components)/page-transformer-components/wrappers/wrapper.tsx
5-app/@right/(_service)/(_types)/page-wrapper-types.ts
6-// @/app/@right/public/(_servise)/(_config)/public-pages-config.ts
7-//@app/@right/public/(_servise)/(_libs)/utils.ts
8-@/app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/header-section.tsx
9-@app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/page-header-elements.tsx
10-@app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/announcement.tsx
11-@app/@right/(_service)/(_components)/page-transformer-components/footer-sections-components/footer-section.tsx
12-@app/@right/(_service)/(_components)/page-transformer-components/body-sections-components/body-section.tsx
2.1. Basic set of components: out-of-the-box template page rendering
Purpose
Standard components represent a basic set of elements for creating typed pages in a unified style. This collection includes architecture-defining elements: dynamic routing, central transformer, wrappers for different types of content, unified header and footer sections, as well as configuration files and typing.
Key principles of standard components:
Interface consistency — all components follow common design and behavior principles
AI compatibility — structure optimized for automatic content generation
SEO optimization — built-in support for search optimization and proper HTML semantics
Typed architecture — strict typing via TypeScript for predictable behavior
Modularity — each component solves a specific task and can be used independently
These components form the foundation of the system, ensuring visual consistency and technical uniformity of all AI-generated pages.
2.1.1. Component /page.tsx: universal catch-all route for dynamic URLs
2.1.1.1. Why Next.js uses catch-all routes: flexibility, fast deployment
A greedy route (catch-all route) is a type of dynamic route in Next.js that allows processing an unlimited number of nested URL segments using a single page component. In the construction [...slug] or (optional) slug array in parameters, all parts of the path will be contained regardless of their number.
Why this is cool:
Arbitrary number of nesting levels.
You don’t need to create separate folders and files in the project structure for every new path. For example, the path /public/category/nike/sneakers/black/12345 will be processed by exactly the same component as /public/test.Flexible navigation and fast launch of new pages.
Just add the appropriate config — and the page instantly appears in the application without creating new components or maintaining a complex file structure.Automation:
This setup perfectly matches auto-generated sidebar/menu: create the necessary item in navigation and the “smart” routing automatically handles displaying the required page.
2.1.1.2. The value of dynamic URLs for AI assistants: infinite content expansion
In modern SaaS/AI products or headless sites there is often a need to quickly create new pages “on the fly” at the request of a user or administrator — the page is built and configured not manually, but through generation (AI or admin panel).
Architecture with a greedy route:
Removes limitations on structure and nesting depth of pages,
Allows working with any number of hierarchy levels (for example, restaurant menus with multi-level categorization; product cards in the structure catalog/category/manufacturer/line/item code),
Provides freedom for users and developers — navigation is built solely on project logic and is not tied to the file structure.
2.1.1.3. Usage examples:
QR-menu for the restaurant:
/public/menu/bar/beer and /public/menu/hot-dishes/italian/pasta → any depth of subcategories.Online store:
/public/shop/clothes/mens/jackets/leather/2023/black/style123Educational portal:
/public/tutorials/python/basics/loops/while-loop/example1
2.1.1.4. How the component page.tsx works
Main tasks:
An array of slugs comes from the URL (for example, ["category", "nike", "sneakers"]).
The function generateStaticParams collects all the routes present in your config (pages-config.ts) and passes them to Next.js for static generation.
The function getPageBySlug quickly finds the required page in the config array by the exact slug received (usually with "public" added at the beginning).
export async function generateStaticParams() {
const pages: PageConfig[] = getAllPublicPages();
return pages.map((page: PageConfig) => ({
slug: page.metadata.slug ? page.metadata.slug.slice(1) : [],
}));
}
Here we form all possible paths for static generation: if the config contains ["public","cat","subcat","product"], the path /public/cat/subcat/product will be generated.
Then, when rendering happens:
export default async function PublicDynamicSlugPage({ params }: Props) {
const resolvedParams = await params;
const slugArr = resolvedParams.slug ?? [];
const pageConfig = getPageBySlug(["public", ...slugArr]);
if (!pageConfig) {
return Page not found;
}
return ;
}
slugArr can be of any length!
"public" is added for correct searching in the array.
The search function works by exact slug match, which excludes collisions.
h5 2.1.1.5. Briefly about key functions:
generateStaticParams — tells Next.js ALL existing nested paths for generation.
getPageBySlug — searches for the required page in the array by exact match of the slug array.
PageHtmlTransformer — a universal renderer that doesn't care about the depth of the page's nesting: it builds the component on the fly from your formalized config.
2.1.1.6. Conclusion. Why this approach was chosen
Flexibility for new sections: no need to think about folders — just add a config and the page will appear!
Convenience of editing and scaling navigation: all routing and navigation are controlled at the data level (slug arrays), not by files.
Real business scenarios: automation of menu, product, course, documentation, blog generation, and anything else that might come to mind.
Standardization for AI assistants: this approach is perfectly suited for automatic content generation based on a text description (prompt) — any new pages appear instantly and are not limited by the route depth.
2.1.1.7. Component
// @/app/@right/public/(_routing)//page.tsx
import { PageHtmlTransformer } from "@/app/@right/(_service)/(_components)/page-transformer-components/page-html-transformer";
import { getPageBySlug } from "@/app/@right/(_service)/(_config)/pages-config";
import { constructMetadata } from "@/lib/construct-metadata";
import type { PageConfig } from "@/app/@right/(_service)/(_types)/page-wrapper-types";
import { getAllPublicPages } from "../../(_servise)/(_libs)/get-all-public-pages";
interface Props {
params: Promise<{ slug?: string[] }>;
}
export async function generateStaticParams() {
const pages: PageConfig[] = getAllPublicPages();
return pages.map((page: PageConfig) => ({
slug: page.metadata.slug || [],
}));
}
export async function generateMetadata({ params }: Props) {
const resolvedParams = await params;
const slugArr = resolvedParams.slug ?? [];
const pageConfig = getPageBySlug(["public", ...slugArr]);
if (!pageConfig) return {};
return constructMetadata(pageConfig.metadata);
}
export default async function PublicDynamicSlugPage({ params }: Props) {
const resolvedParams = await params;
const slugArr = resolvedParams.slug ?? [];
const publicPageConfig = getPageBySlug(["public", ...slugArr]);
if (!publicPageConfig) {
return Page not found;
}
return ;
}
2.1.1.7. TL;DR on catch-all: when and how to use it in an AI project
Greedy routes and centralized page config make your application architecture as flexible, extensible, and friendly as possible for both developers and AI content orchestration. New page = new line in the config, no new files or folders — navigation and rendering adjust automatically!
2.1.2. Real catch-all route use cases: store, restaurant menu, educational portal
2.1.2.1. Architectural role and purpose of the component
PageHtmlTransformer is the core of the entire rendering system — a universal "conductor" that takes a declarative page description (PageConfig) and turns it into live React components. Its main task is to ensure visual consistency while maximizing content flexibility.
h6 Key functions:
Centralized rendering logic — a single component manages the entire section display process
Typed handling — each section type receives the appropriate wrapper and rendering
Theme and responsiveness management — central point for passing contextual data
Style unification — ensures header/footer section consistency across all pages
2.1.2.2. Fundamental decision: why header and footer are separated
The decision to move HeaderSection and FooterSection outside the default BodySection logic is driven by fundamental design consistency requirements:
h6 Problem with AI content generators:
Automated AI generators often produce inconsistent content in headers and conclusions formatting
Each section can have unique body content, but the start and end must follow strict brand standards
Header and Footer are "style anchors" that set the tone for the entire section and ensure visual coherence
Architectural benefits:
Guaranteed consistency — regardless of how AI generates body content, header and footer will always follow corporate style
Centralized control — changes in header styles apply instantly across all pages
Predictable structure — developers and designers can rely on stable patterns
2.1.2.3. Theme and context management
export function PageHtmlTransformer({ data }: PageHtmlTransformerProps) {
const { theme } = useTheme();
// ... rest of logic
}
Why pass the theme specifically from here:
Scenarios requiring theme-aware logic:
Conditional images: One image is used in light theme, another in dark
Dynamic styles: Components with special color scheme logic
Animations and transitions: Different effects for light/dark modes
Similarly with the mobile version (isMobile):
Conditional rendering: Some components are completely disabled on mobile
Radically different layout: When Tailwind CSS is not flexible enough
Performance: Avoiding heavy elements on weak devices
Usage example:
// In a child component
{theme === 'dark' ? : }
{!isMobile && }
2.1.2.4. Two types of Wrapper: why differentiation is needed
FullScreenWrapper vs Wrapper — fundamental differences:
FullScreenWrapper (for hero-section and similar):
Full-screen layout — takes up the entire viewport height
Supports background media — video/image backgrounds with content overlay
Z-index management — complex layered structure for overlay effects
Positioning — absolute/relative positioning for centering content
Wrapper (standard):
Sectional layout — regular blocks with padding/margin
Container-based — standard grid and spacing
Simplicity and performance — minimal CSS properties
Why they cannot be merged into a single component:
CSS conflicts — fullscreen and container logic are incompatible
Performance — extra styles affect rendering
Semantic clarity — different tasks require different approaches
Maintenance — easier to maintain two specialized components
2.1.2.5. Limitations of renderSectionContent and custom cases
Problems with the standard renderSectionContent function:
function renderSectionContent(config: any) {
return (
<>
{config.headerContent && }
{config.bodyContent && {config.bodyContent.content} }
{config.footerContent && }
>
);
}
Limitations of this approach:
No state management — no way to use useState, useEffect
Static data — content is passed as ready data, without interactivity
No event handling — unable to process user interactions
No lifecycle methods — components cannot respond to mount/unmount
When custom cases are needed:
Interactive forms — components with validation and data submission
Animated sections — complex transitions and animations
Real-time data — components subscribing to WebSocket or API
Conditional logic — complex calculations based on user input
Solution through custom cases:
switch (section.type) {
case "interactive-form-section": {
return ;
}
case "real-time-dashboard": {
return ;
}
// standard cases...
default: {
return (
{renderSectionContent(config)}
);
}
}
2.1.2.6. Architectural Role and Workflow Analysis
PageHtmlTransformer performs several key architectural roles:
Facade pattern — provides a simple interface for a complex rendering system
Factory pattern — creates the required components based on section type
Strategy pattern — chooses the rendering strategy (Wrapper/FullScreenWrapper) depending on the context
Template Method pattern — defines the skeleton of the rendering algorithm with the ability to override individual steps
Workflow:
Configuration retrieval — accepts PageConfig with an array of sections
Context initialization — obtains theme, determines isMobile
Iteration over sections — determines the type and rendering method for each section
Wrapper selection — FullScreenWrapper for hero sections, Wrapper for others
Content rendering — applies renderSectionContent or a custom component
Final page assembly — combines all sections into a unified structure
2.1.2.7. Component
// @/app/@right/(_service)/(_components)/page-transformer-components/page-html-transformer.tsx
"use client";
import {
PageConfig,
Section,
SectionConfig,
} from "../../(_types)/page-wrapper-types";
import { BodySection } from "./body-sections-components/body-section";
import { FooterSection } from "./footer-sections-components/footer-section";
import { HeaderSection } from "./header-sections-components/header-section";
import { useTheme } from "next-themes";
import { FullScreenWrapper } from "./wrappers/full-screen-wrapper";
import { Wrapper } from "./wrappers/wrapper";
interface PageHtmlTransformerProps {
data: PageConfig;
}
function renderSectionContent(config: any) {
return (
<>
{config.headerContent && (
)}
{config.bodyContent && (
{config.bodyContent.content}
)}
{config.footerContent && (
)}
>
);
}
export function PageHtmlTransformer({ data }: PageHtmlTransformerProps) {
const { theme } = useTheme();
if (!data?.sections?.length) return null;
return (
<>
{data.sections.map((section: Section, idx: number) => {
switch (section.type) {
case "hero-section": {
const config = section as SectionConfig;
return (
{renderSectionContent(config)}
);
}
// ...other cases unchanged
default: {
const config = section as any;
return (
{renderSectionContent(config)}
);
}
}
})}
>
);
}
2.1.2.7.Conclusion
PageHtmlTransformer is the heart of the system that turns declarative descriptions into live, interactive, and stylistically consistent web pages. Its architecture balances flexibility (support for custom components) and consistency (standardized header/footer), making it an ideal tool for AI-driven content generation.
2.1.3. FullScreenWrapper vs Wrapper: when to use a fullscreen wrapper
2.1.3.1. Primary Purpose
FullScreenWrapper is designed for sections requiring full-screen display with support for video/images in the background. It is a basic container for hero sections where content overlays media elements.
2.1.3.2. Layered Structure
{backgroundElement} // z-0
{children} // Header → Body → Footer
Technical features:
min-h-screen — ensures filling the entire viewport
relative positioning for z-index management
Background elements (video/image) with absolute and z-0
Content with relative z-10 is always above the background
2.1.3.3. Customization and Extensions
Place for gradients and animations: Add effects via className prop:
Background transparency control: Video automatically gets opacity-40 for text readability, but this is easy to override.
Transition animations: transition-all duration-500 is already built-in for smooth background changes.
2.1.3.4. Usage for Custom Templates
If you need dividers between sections, borders, highlighting, or blur control — FullScreenWrapper is the perfect place to implement it:
// Example with border and gradient
2.1.3.5. Component
// @/app/@right/(_service)/(_components)/page-transformer-components/wrappers/full-screen-wrapper.tsx
import React, { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
interface FullScreenWrapperProps extends HTMLAttributes {
videoUrl?: string;
imageUrl?: string;
className?: string;
children: React.ReactNode;
}
export function FullScreenWrapper({
videoUrl,
imageUrl,
className,
children,
...props
}: FullScreenWrapperProps) {
let backgroundElement: React.ReactNode = null;
if (videoUrl) {
backgroundElement = (
);
} else if (imageUrl) {
backgroundElement = (
);
}
return (
{backgroundElement}
{children}
);
}
2.1.4. Wrapper: basic section shell with container mx-auto
2.1.4.1. Purpose
Wrapper is a basic shell for most content sections. It provides standard paddings, content centering, and containerization.
2.1.4.2. Structure
{children} // Header → Body → Footer
Key parameters:
py-10 lg:py-14 — adaptive vertical paddings
container mx-auto px-4 — centered content with horizontal paddings
bg-background — uses the theme CSS variable
2.1.4.3. Customization Points
Background effects:
Easily added via className:
<Wrapper className="bg-gradient-to-r from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
Dividers between sections:
Fade-in animations:
Blur and highlight control:
2.1.4.4. Medium-custom reusable templates
When you need a template with unique styling but a standard structure — Wrapper is your choice:
// Example for pricing sections
2.1.3.5. Component
// app/@right/(_service)/(_components)/page-transformer-components/wrappers/wrapper.tsx
import React, { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
interface WrapperProps extends HTMLAttributes {
className?: string;
children: React.ReactNode;
}
export function Wrapper({ className, children, ...props }: WrapperProps) {
return (
{children}
);
}
Why two separate wrappers?
Technical reasons:
FullScreenWrapper contains complex media background logic and z-index management
Wrapper is optimized for performance — minimal CSS rules
Different positioning approaches: relative vs static
Incompatible CSS properties: min-h-screen vs py-*
Practical benefits:
Clear separation of responsibilities
Ease of customization for specific tasks
Predictable behavior for AI generation
No CSS conflicts between types of sections
For AI systems: Simple selection rule — immersive content (hero, showcases) = FullScreenWrapper, informational content = Wrapper.
2.1.5. page-wrapper-types.ts: TypeScript contract between AI and UI
2.1.5.1. The role of typing: a contract between AI and rendering.
The page-wrapper-types.ts file is a formal contract between the AI generator and the rendering system. It defines the architecture of all possible page components and the rules of their interaction.
2.1.5.2. SectionType categories and AI generation.
How AI works: Artificial intelligence first analyzes the available section types from the enum SectionType, then decides which ones to add to a specific page based on the user request.
It is expected that for each of the 25+ section types in the knowledge base, the AI will have exhaustive instructions and examples loaded, sufficient for autonomous content generation.
export type SectionType =
| "hero-section" | "cta-section" | "faq-section"
| "features-section" | "testimonials-section" | "pricing-section"
// ... and other types
2.1.5.3. SEO optimization and metadata.
PageMetadata ensures correct formation of meta tags for search engines:
export interface PageMetadata {
id: string; // Unique identifier
title: string; // tag for SEO
description: string; // for snippets
image?: string; // Open Graph image
slug?: string[]; // Page URL structure
type: SectionType; // Main section type
}
Automatic generation of meta tags occurs in the generateMetadata() function, which retrieves data from the config and passes it to constructMetadata() to form correct HTML headings.
2.1.5.4. HeaderContentConfig: control of SEO hierarchy.
Critically important for SEO:
interface HeaderContentConfig {
heading: string;
headingLevel?: 1 | 2; // H1/H2 for search hierarchy
description?: string;
showBorder?: boolean;
}
Be careful with headings: The Header section is optional, since in some types of sections H1/H2 headings can be placed inside bodyContent. This is potentially dangerous for interface consistency — the primary goal of this tutorial.
2.1.5.5. BodySection: maximum design freedom.
bodyContent?: React.ReactNode;
Architectural solution: The body of the section gets full freedom to implement any design, while the Header and Footer are strictly standardized to maintain a unified style.
interface FooterContentConfig {
actions?: {
label: string;
href: string;
variant?: "default" | "secondary" | "outline" | "ghost" | "link";
}[];
}
2.1.5.6. FooterContentConfig: minimalism in design.
The current implementation only assumes optional action buttons. No additional elements — maximum simplicity and consistency.
2.1.5.7. Principles of extending typing.
To add new SectionType:
AI receives new instructions in the knowledge base
The type is added to the enum SectionType
If necessary, the configuration interface is extended
Balance of standardization: Header and Footer are strictly typed for SEO and consistency, Body gets maximum flexibility for creative solutions from AI generators
2.1.5.8. Component
2.1.5.9. customComponentsAnyTypeData: support for arbitrary custom components
The field customComponentsAnyTypeData?: any; in SectionConfig is intended for passing data into sections implemented via complex or unique custom components that do not fit into standard interfaces.
The type and structure of this data must be thoroughly and strictly described inside the custom component itself. This solution provides flexibility for integrating interactive, dynamic, and advanced user sections without violating the overall typing contract of the main page architecture.
// app/@right/(_service)/(_types)/page-wrapper-types.ts
export interface MetadataConfig {
title?: string;
description?: string;
}
export interface PageMetadata {
id: string;
title: string;
description: string;
image?: string;
slug?: string[];
type: SectionType;
}
export type SectionType =
| "hero-section"
| "cta-section"
| "faq-section"
| "features-section"
| "testimonials-section"
| "pricing-section"
| "contact-section"
| "blog-posts-section"
| "product-grid-section"
| "image-gallery-section"
| "text-block-section"
| "video-section"
| "team-section"
| "about-us-section"
| "newsletter-section"
| "social-proof-section"
| "comparison-table-section"
| "map-section"
| "custom-html-section"
| "changelog-section"
| "comparison-two-column-section"
| "comparison-three-column-section"
| "feature-showcase-section";
export interface BaseSection {
id: string;
type: SectionType;
className?: string;
}
export interface HeaderContentConfig {
announcement?: {
badgeText?: string;
descriptionText?: string;
href?: string;
};
heading: string;
headingLevel?: 1 | 2;
description?: string;
showBorder?: boolean;
}
export interface FooterContentConfig {
actions?: {
label: string;
href: string;
variant?:
| "default"
| "secondary"
| "destructive"
| "outline"
| "ghost"
| "link";
}[];
}
export interface SectionConfig extends BaseSection {
type: SectionType;
headerContent: HeaderContentConfig;
bodyContent?: React.ReactNode;
footerContent?: FooterContentConfig;
videoUrl?: string;
imageUrl?: string;
sectionClassName?: string;
contentWrapperClassName?: string;
customComponentsAnyTypeData?: any;
}
export type Section = SectionConfig;
export interface PageConfig {
metadata: PageMetadata;
sections: Section[];
}
export type SlugType = string[];
2.1.6. public-pages-config.ts: registry of public pages and Slug routes
2.1.6.1. Role of the config in the system architecture
The file public-pages-config.ts is the result of artificial intelligence — an automatically generated registry of all public pages of the application. This config is created by AI automatically based on system instructions and user requests and directly depends on the available components in the examples of the knowledge base and listings in the SectionType typing, which we discussed above. The generated structure serves as a bridge between AI analysis of user requirements and the page rendering system.
2.1.6.2. When to use a file config
Optimal scenarios for a config file:
Projects with no more than 10-15 pages
Static content that rarely changes
Prototyping and MVPs — quick start without a database
Landing pages with a fixed structure
When to switch to a database:
In the production version of a large project, the file approach makes sense only with a small number of pages. Most cases require storing this information in a database for dynamic content management.
2.1.6.3. Config structure
export const PublicPagesConfig = {
pages: [
{
metadata: {
id: "public",
title: "Enterprise-Grade AI Next.js starter",
description: "Free Open-Source starter kit...",
slug: ["public", "test"],
type: "hero-section",
},
sections: [
{
id: "test-block",
type: "hero-section",
headerContent: { /* SEO headers */ },
bodyContent: {},
footerContent: { /* Action buttons */ },
videoUrl: "/_static/video/ai-loop.mp4",
contentWrapperClassName: "text-white",
} as SectionConfig,
],
},
] as PageConfig[],
};
2.1.6.4. Categorization principle for configs
Important architectural decision: Placing the config in the directory public/(_service)/(_config)/ indicates its affiliation with public pages.
Scaling by categories:
public-pages-config.ts — public pages (landing, about, contact)
docs-pages-config.ts — documentation with extended navigation
admin-pages-config.ts — admin panels
blog-pages-config.ts — blog posts and articles
Each category gets:
A dedicated config file
Specialized section types
Unique rendering logic
2.1.6.5. Interaction with AI generation
For AI systems, the config serves as:
A structure template — how PageConfig objects should look
A data example — reference values for generation
Type validation — strict typing through TypeScript
Workflow:
AI analyzes the existing config
Generates a new PageConfig based on the prompt
The developer adds the config to the pages array
The system automatically picks up the new routes
2.1.6.6. Advantages and limitations
Advantages of the file approach:
Versioning via Git
Type safety at compile time
Zero latency — no database queries
Easy deployment — static generation
Limitations:
No dynamic management
Rebuild required on changes
Not suitable for user-generated content
Scaling issues beyond 20-30 pages
2.1.6.7. Component
// @/app/@right/public/(_servise)/(_config)/public-pages-config.ts
import {
PageConfig,
SectionConfig,
} from "@/app/@right/(_service)/(_types)/page-wrapper-types";
export const PublicPagesConfig = {
pages: [
{
metadata: {
id: "public",
title: "Enterprise-Grade AI Next.js starter",
description: "Free Open-Source starter kit...",
slug: ["public", "test"],
type: "hero-section",
},
sections: [
{
id: "test-block",
type: "hero-section",
headerContent: {
announcement: {
badgeText: "Thanks",
descriptionText: "AI-SDK V5 & Vercel AI",
href: "https://github.com/aifa-agi/aifa",
},
heading: "Enterprise-Grade AI Next.js starter",
description:
"Free Open-Source starter kit to build, deploy, and scale intelligent AI applications. Artifacts Feature, features secure multi-provider auth, Stripe payments, vector knowledge bases, deep-research agents, and a unique fractal architecture designed for the future of AI.",
showBorder: false,
headingLevel: 1,
},
bodyContent: {},
footerContent: {
actions: [
{
label: "Get Started",
href: "/https://github.com/aifa-agi/aifa",
variant: "default",
},
{ label: "Browse Docs", href: "/docs", variant: "ghost" },
],
},
videoUrl: "/_static/video/ai-loop.mp4",
contentWrapperClassName: "text-white",
} as SectionConfig,
],
},
] as PageConfig[],
};
2.1.6.7. Conclusion
public-pages-config.ts is a practical solution for small and medium-sized projects, providing a balance between ease of management and functionality. For large projects, this approach serves as an excellent starting point before migrating to a database-driven architecture.
2.1.7. utils.ts: helper configuration functions
Utilities for working with page configs. Contains two key functions: getAllPublicPages() — filters all pages with the "public" prefix for static generation, and getPageBySlug() — finds a specific page by exact match of the slug array, case-insensitive.
// @app/@right/public/(_servise)/(_libs)/utils.ts
import {
PageConfig,
SlugType,
} from "@/app/@right/(_service)/(_types)/page-wrapper-types";
import { PublicPagesConfig } from "../(_config)/public-pages-config";
export function getAllPublicPages(): PageConfig[] {
return PublicPagesConfig.pages.filter(
(page: PageConfig) => page.metadata.slug?.[0] === "public"
);
}
export function getPageBySlug(slug: SlugType): PageConfig | undefined {
return PublicPagesConfig.pages.find(
(page: PageConfig) =>
JSON.stringify(
page.metadata.slug?.map((s: string) => s.toLowerCase())
) === JSON.stringify(slug.map((s: string) => s.toLowerCase()))
);
}
2.1.8. header-section.tsx: unified section header
Standardized header component for all section types. Provides consistency through optional elements: announcement, H1/H2 heading, description. Plays a key role in SEO optimization and visual consistency of AI-generated pages.
// @/app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/header-section.tsx
import React from "react";
import { cn } from "@/lib/utils";
import {
PageHeaderDescription,
PageHeaderHeading,
} from "./page-header-elements";
import { Announcement } from "./announcement";
import { HeaderContentConfig } from "../../../(_types)/page-wrapper-types";
export type HeaderSectionProps = {
headerContent: HeaderContentConfig;
} & React.HTMLAttributes;
export function HeaderSection({
headerContent,
className,
...props
}: HeaderSectionProps) {
if (!headerContent) return null;
const {
announcement,
heading,
headingLevel = 1,
description,
showBorder = false,
} = headerContent;
return (
{announcement && (
)}
{heading}
{description && (
{description}
)}
);
}
2.1.9. header-section.tsx: unified section header
Atomic components for building headers: PageHeaderHeading with H1/H2 support, PageHeaderDescription for subtitles, PageActions for placing buttons. They use createElement for dynamically generating HTML tags with the correct SEO hierarchy.
// @app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/page-header-elements.tsx
import { HTMLAttributes, createElement } from "react";
import { cn } from "@/lib/utils";
type HeadingTag = "h1" | "h2";
interface PageHeaderHeadingProps extends HTMLAttributes {
level?: 1 | 2;
}
function PageHeaderHeading({
className,
level = 1,
...props
}: PageHeaderHeadingProps) {
// Choosing tag by level
const Heading: HeadingTag = level === 1 ? "h1" : "h2";
const h1Classes = "text-2xl sm:text-3xl md:text-6xl lg:text-7xl";
const h2Classes = "text-lg sm:text-xl md:text-3xl lg:text-4xl";
// Correct use of createElement
return createElement(Heading, {
className: cn(
"text-center font-bold leading-tight tracking-tighter font-serif",
level === 1 ? h1Classes : h2Classes,
className
),
...props,
});
}
function PageHeaderDescription({
className,
...props
}: HTMLAttributes) {
return (
);
}
function PageActions({ className, ...props }: HTMLAttributes) {
return (
);
}
// Export components
export { PageActions, PageHeaderDescription, PageHeaderHeading };
2.1.10. announcement.tsx: announcement component
Interactive badge for announcements and notifications. Supports navigation, keyboard control, and conditional rendering. Automatically hides if there is no content. Used to draw attention to important updates or links.
// @app/@right/(_service)/(_components)/page-transformer-components/header-sections-components/announcement.tsx
"use client";
import { useRouter } from "next/navigation";
import { Badge } from "@/components/ui/badge";
import { ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";
interface AnnouncementProps {
badgeText?: string; // Made optional
descriptionText?: string; // Made optional
href?: string; // Made optional
className?: string;
}
export function Announcement({
badgeText,
descriptionText,
href,
className,
}: AnnouncementProps) {
const router = useRouter();
const handleOnClick = () => {
if (href) {
router.push(href);
}
};
// If no badge text, description, or href, return null
if (!badgeText && !descriptionText && !href) {
return null;
}
return (
{
if (e.key === "Enter" || e.key === " ") {
handleOnClick();
}
}}
>
{badgeText && (
{badgeText}
)}
{descriptionText && (
{descriptionText}
)}
{href && }
);
}
2.1.11. footer-section.tsx: unified actions for sections
Standardized footer with action buttons. Supports multiple buttons with different styling options. Ensures consistency of CTA elements across all sections. Automatically hides when there are no actions.
// @app/@right/(_service)/(_components)/page-transformer-components/footer-sections-components/footer-section.tsx
"use client";
import { useRouter } from "next/navigation";
import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { PageActions } from "../header-sections-components/page-header-elements";
interface FooterAction {
label: string;
href: string;
variant?:
| "default"
| "secondary"
| "destructive"
| "outline"
| "ghost"
| "link";
}
interface FooterSectionProps extends HTMLAttributes {
actions?: FooterAction[];
}
export function FooterSection({
actions,
className,
...props
}: FooterSectionProps) {
const router = useRouter();
if (!actions || actions.length === 0) {
return null;
}
return (
{actions.map((action) => (
))}
);
}
2.1.12. body-section.tsx: container for arbitrary content
2.1.12.1. Purpose and Philosophy
BodySection is an extremely flexible container for the main content of sections. Unlike the strictly standardized Header and Footer, this component provides full freedom for placing any React content.
2.1.12.2. Principles of content generation
Critically important: Content for bodyContent must be generated as regular TSX without .map() elements, where all elements are presented in expanded form:
// ✅ Correct - expanded TSX
bodyContent: (
<>
Feature 1
Description 1
Feature 2
Description 2
Feature 3
Description 3
>
)
// ❌ Incorrect - using .map()
bodyContent: (
{features.map(feature => )}
)
2.1.12.3. When to use custom components
If a section requires complex logic (state, effects, interactivity), create a standalone component and add it to PageHtmlTransformer as a separate
case:
switch (section.type) {
case "interactive-pricing-section": {
return ;
}
// standard cases...
}
2.1.12.3. Technical implementation
The component checks for the presence of children and renders them without additional processing. If there is no content, it returns null, ensuring clean markup without empty blocks.
// @/app/@right/(_service)/(_components)/page-transformer-components/body-sections-components/body-section.tsx
export function BodySection({ children, className, ...props }: BodySectionProps) {
const hasChildren = children !== null && children !== undefined &&
!(Array.isArray(children) && children.length === 0) &&
!(typeof children === "string" && children === "");
if (!hasChildren) return null;
return (
{children}
);
}
2.1.12.4. Component
// @app/@right/(_service)/(_components)/page-transformer-components/body-sections-components/body-section.tsx
import type { HTMLAttributes, ReactNode } from "react";
import { cn } from "@/lib/utils";
import { SectionType } from "../../../(_types)/page-wrapper-types";
interface BodySectionProps extends HTMLAttributes {
children?: ReactNode | null;
type: SectionType;
}
/**
* BodySection component.
* Renders children if present,
* otherwise renders an empty section with a default height (in rem) if provided,
* or renders nothing.
*/
export function BodySection({
children,
className,
...props
}: BodySectionProps) {
const defaultHeightRem = 0;
const hasChildren =
children !== null &&
children !== undefined &&
// Covers case when children = [] or ""
!(Array.isArray(children) && children.length === 0) &&
!(typeof children === "string" && children === "");
if (!hasChildren && defaultHeightRem) {
return (
{/* Empty section with default height */}
);
}
if (!hasChildren) {
return null;
}
// Normal case: render content
return (
{children}
);
}
2.1.12.5. ConclusionBodySection acts as a bridge between the standardized system architecture and the creative freedom of AI generation, providing a balance between control and flexibility.
2.2. Custom complex componentsCustom components are designed to implement complex interactive functionality beyond the capabilities of the standard architecture. These components are used when state management, advanced animations, real-time interaction, or unique business logic is required.
h4 When to use Custom components:
Interactive elements — forms with validation, calculators, product configurators
Animated presentations — complex transitions, parallax effects, automatic sliders
Real-time functionality — chats, notifications, live data
Adaptive logic — components with radically different behavior on desktop/mobile
Unique UI patterns — non-standard interface elements, project-specific
Integration with architecture:
Custom components are added to PageHtmlTransformer as separate cases in a switch statement, gaining access to the context of the theme, device, and other system parameters. They maintain a consistent style through the use of shared CSS classes and design tokens, but are given complete freedom to implement internal logic.
This approach ensures a balance between standardizing the majority of content and creative freedom in implementing complex interactive elements.
2.2.1. Directive for handling custom components
2.2.1.1. About Wrappers
Prohibition of wrappers for custom components
Custom components should be returned directly without a Wrapper or FullScreenWrapper, as they manage their own structure, padding, and layers.
2.2.1.2. About custom component types
Placement of types in the component
The prop interfaces of the custom component are declared at the top of the component itself. Data is passed through the customComponentsAnyTypeData field in the section config.
2.2.1.3 Extended SectionType:
Adding a new section type
A unique type is added for each custom component to the enum SectionType in the page-wrapper-types.ts file for correct typing and handling in the switch statement.
// @/app/@right/(_service)/(_types)/page-wrapper-types.ts
// ...
export type SectionType ="hero-section" | “new-custom-section”;
2.2.1.4. Update cases in the PageHtmlTransformer
Handling a custom case
A new case is added to the PageHtmlTransformer to handle the custom section type with direct data transfer from customComponentsAnyTypeData to the component's props.
case "new-custom-section":
return (
);
2.2.1.5. Custom Config
Configuration structure
Example of a page configuration with a custom section, where all specific data is placed in the customComponentsAnyTypeData object with an arbitrary structure to meet the component's needs.
export const PublicPagesConfig = {
pages: [
{
metadata: {
id: "CIUD",
title: "1",
description: "2",
slug: ["public", "name"],
type: "new-custom-section",
},
sections: [
{
id: "new-custom-section",
type: "new-custom-section",
customComponentsAnyTypeData: {
metaData: {
metaTitle: "1",
metaDescription: "2",
},
customData: {
mediaUrl: "/_static/illustrations/3.png",
title: "4",
description:
"5",
},
},
} as SectionConfig,
],
},
] as PageConfig[],
2.2.2. Custom component example
2.3.2.1. DoublePresentation
2.3.2.1.1. Updating main types
// @/app/@right/(_service)/(_types)/page-wrapper-types.ts
// Add new section type
export type SectionType =
| "hero-section"
| "cta-section"
| "double-presentation-section" // New type
// ... other types
2.3.2.1.2. Add new case with current data to : PageHtmlTransformer
case "double-presentation-section":
return (
);
2.3.2.1.3. page-wrapper-types.ts
// app/@right/(_service)/(_types)/page-wrapper-types.ts
export interface MetadataConfig {
title?: string;
description?: string;
}
export type CuidString = string;
export interface PageMetadata {
id: CuidString;
title: string;
description: string;
image?: string;
slug?: string[];
type: SectionType;
}
export type SectionType =
| "hero-section"
| "cta-section"
| "faq-section"
| "features-section"
| "testimonials-section"
| "pricing-section"
| "contact-section"
| "blog-posts-section"
| "product-grid-section"
| "image-gallery-section"
| "text-block-section"
| "video-section"
| "team-section"
| "about-us-section"
| "newsletter-section"
| "social-proof-section"
| "comparison-table-section"
| "map-section"
| "custom-html-section"
| "changelog-section"
| "comparison-two-column-section"
| "comparison-three-column-section"
| "feature-showcase-section"
| "double-presentation-section";
export interface BaseSection {
id: string;
type: SectionType;
className?: string;
}
export interface HeaderContentConfig {
announcement?: {
badgeText?: string;
descriptionText?: string;
href?: string;
};
heading: string;
headingLevel?: 1 | 2;
description?: string;
showBorder?: boolean;
}
export interface FooterContentConfig {
actions?: {
label: string;
href: string;
variant?:
| "default"
| "secondary"
| "destructive"
| "outline"
| "ghost"
| "link";
}[];
}
export interface SectionConfig extends BaseSection {
type: SectionType;
headerContent: HeaderContentConfig;
bodyContent?: React.ReactNode;
footerContent?: FooterContentConfig;
videoUrl?: string;
imageUrl?: string;
sectionClassName?: string;
contentWrapperClassName?: string;
customComponentsAnyTypeData?: any;
}
export type Section = SectionConfig;
export interface PageConfig {
metadata: PageMetadata;
sections: Section[];
}
export type SlugType = string[];
2.3.2.1.4. Config, example:
// @/app/@right/public/(_servise)/(_config)/public-pages-config.ts
import {
PageConfig,
SectionConfig,
} from "@/app/@right/(_service)/(_types)/page-wrapper-types";
export const PublicPagesConfig = {
pages: [
{
metadata: {
id: "public",
title: "Enterprise-Grade AI Next.js starter",
description: "Free Open-Source starter kit...",
slug: ["public", "test"],
type: "hero-section",
},
sections: [
{
id: "test-block",
type: "hero-section",
headerContent: {
announcement: {
badgeText: "Thanks",
descriptionText: "AI-SDK V5 & Vercel AI",
href: "https://github.com/aifa-agi/aifa",
},
heading: "Enterprise-Grade AI Next.js starter",
description:
"Free Open-Source starter kit to build, deploy, and scale intelligent AI applications. Artifacts Feature, features secure multi-provider auth, Stripe payments, vector knowledge bases, deep-research agents, and a unique fractal architecture designed for the future of AI.",
showBorder: false,
headingLevel: 1,
},
bodyContent: {},
footerContent: {
actions: [
{
label: "Get Started",
href: "/https://github.com/aifa-agi/aifa",
variant: "default",
},
{ label: "Browse Docs", href: "/docs", variant: "ghost" },
],
},
videoUrl: "/_static/video/ai-loop.mp4",
contentWrapperClassName: "text-white",
} as SectionConfig,
],
},
{
metadata: {
id: "interactive-ai",
title: "Interactive AI Demo",
description: "Demo: DoublePresentation custom case",
slug: ["public", "example"],
type: "double-presentation-section",
},
sections: [
{
id: "double-presentation-demo",
type: "double-presentation-section",
customComponentsAnyTypeData: {
metaData: {
metaTitle: "Interactive AI: Where Conversation Builds the UI",
metaDescription: "Discover what makes AIFA revolutionary...",
},
leftItem: {
mediaUrl: "/_static/illustrations/ai-chat.png",
title: "Ai Artifacts Chatbot",
description:
"As the AI chatbot speaks, it highlights elements...",
},
rightItem: {
mediaUrl: "/_static/illustrations/ai-web.png",
title: "Related Pages",
description:
"Click any UI element, and the AI provides instant context...",
},
},
} as SectionConfig,
],
},
] as PageConfig[],
};
2.3.2.1.5. Component
// @/app/@right/(_service)/(_components)/page-transformer-components/custom-sections/custom-double-prsentation.tsx
"use client";
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import Image from "next/image";
import { cn } from "@/lib/utils";
import { useMediaQuery } from "@/hooks/use-media-query";
interface PresentationMeta {
metaTitle: string;
metaDescription: string;
}
interface PresentationItem {
mediaUrl: string;
title: string;
description: string;
}
interface DoublePresentationProps {
metaData: PresentationMeta;
leftItem: PresentationItem;
rightItem: PresentationItem;
}
export default function DoublePresentation({
metaData,
leftItem,
rightItem,
}: DoublePresentationProps) {
const { isMobile } = useMediaQuery();
// Desktop animation state
const [activeContainer, setActiveContainer] = useState<"left" | "right">(
"left"
);
const [sliderKey, setSliderKey] = useState(0);
// Desktop auto-switching effect
useEffect(() => {
// Only run animation cycle on desktop
if (isMobile) return;
let sliderTimer: NodeJS.Timeout;
let transitionTimer: NodeJS.Timeout;
const startAnimationCycle = () => {
setSliderKey((prev) => prev + 1);
sliderTimer = setTimeout(() => {
setActiveContainer((prev) => (prev === "left" ? "right" : "left"));
transitionTimer = setTimeout(() => {
startAnimationCycle();
}, 500);
}, 9000);
};
startAnimationCycle();
return () => {
clearTimeout(sliderTimer);
clearTimeout(transitionTimer);
};
}, [isMobile]);
// Return null while determining screen size
if (isMobile === null) {
return null;
}
// Common CSS classes
const metaBlockClass = "text-center max-w-3xl flex flex-col items-center";
const descriptionClass =
"mb-12 max-w-xl text-base text-muted-foreground text-center";
const desktopTitleClass =
"mb-6 max-w-3xl font-serif font-bold leading-tight md:text-2xl lg:text-4xl";
const desktopDescriptionClass =
"mb-12 max-w-xl text-lg text-muted-foreground md:text-xl text-center";
// Mobile card renderer
const renderMobileCard = (item: PresentationItem) => (
{item.title}
{item.description}
);
// Desktop card renderer
const renderDesktopCard = (item: PresentationItem, isActive: boolean) => (
{item.title}
{item.description}
);
// Mobile layout
if (isMobile) {
return (
{metaData.metaTitle}
{metaData.metaDescription}
{renderMobileCard(leftItem)}
{renderMobileCard(rightItem)}
);
}
// Desktop layout
return (
{metaData.metaTitle}
{metaData.metaDescription}
{renderDesktopCard(leftItem, activeContainer === "left")}
{renderDesktopCard(rightItem, activeContainer === "right")}
);
}
Roma Armstromg
AI Architector
As always, I present the source code of the ready-made solution so that you can immediately start creating blogs, stores, landing pages, etc. https://github.com/aifa-agi/aifa
Write comment