How to Use GeminiAI Generative Content to Populate a Predefined Skeleton for Landing Pages

Building a Dynamic Pricing Section for Your Landing Page with Ease.

Xian Li
Bootcamp
5 min read2 days ago

--

In my previous posts, I talked about the basic structure of my project, and how I took advantage of the SVG to dynamically generate shapes and cropped images in a relative manner. In today’s post, I would like to walk you through my approach to dynamically generating the pricing section using the GeminAI generative content and populating it into my skeleton. Take a look at my demo pricing section.

demo one
demo two

Since I came across the technique about how to utilize the schema as text in the prompt before, I started to generate quite predictable output. Here is the part of my prompt,

`
if a section includes Pricing, only use pricingSchema for that section, and name it pricingSchema, using the predefined JSON schema:
Pricing = {'plan': string, 'price': number,'heading': string, 'subheading': string,}
Return: Array<Pricing>

`

Using schema can make sure the output is more predictable. The data that are fetched are always like this,

screenshot

At the heart of this project is a skeleton that defines the layout of the landing page. Take a look at my helper function, generateRandomGrid()

export function generateRandomGrid(dynamicSections: Record<string, any> = {}) {
const randomGrid: JSX.Element[] = [];

const fixedSections: string[] = ['Header'];
const fixedSectionsTwo: string[] = ['Newsletter', 'Footer'];

if (
!dynamicSections ||
!Array.isArray(dynamicSections) ||
dynamicSections.length === 0
)
return randomGrid;

const rows: number = Math.floor(Math.random() * 3) + 1; // Random number between 1 and 3

// Render fixed sections like Header
for (let i = 0; i < fixedSections.length; i++) {
const columnsOne: number = Math.floor(Math.random() * 12) + 1; // Random number between 1 and 12
const gridStyleOne: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: `repeat(${columnsOne}, 1fr)`,
gridTemplateRows: `repeat(${rows}, 1fr)`,
marginBottom: '40px',
position: 'relative',
width: '100%',
height: '100vh',
};
if (fixedSections[i] === 'Header') {
gridStyleOne.height = '20vh';
}

randomGrid.push(
<div key={fixedSections[i]} style={gridStyleOne}>
{fixedSections[i]}
</div>
);
}

// Render dynamic sections
dynamicSections.forEach((section: any) => {
const columnsOne: number = Math.floor(Math.random() * 12) + 1; // Random number between 1 and 12
const gridStyleOne: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: `repeat(${columnsOne}, 1fr)`,
gridTemplateRows: `repeat(${rows}, 1fr)`,
marginBottom: '40px',
position: 'relative',
width: '100%',
height: '80vh',
};
const columnsTwo: number = Math.floor(Math.random() * 12) + 1; // Random number between 1 and 12
const gridStyleTwo: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: `repeat(${columnsTwo}, 1fr)`,
gridTemplateRows: `repeat(${rows}, 1fr)`,
paddingTop: '20px',
paddingBottom: '20px',
position: 'relative',
width: '100%',
height: 'min-content',
};

const columnsThree: number = Math.floor(Math.random() * 12) + 1; // Random number between 1 and 12
const gridStyleThree: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: `repeat(${columnsThree}, 1fr)`,
gridTemplateRows: `repeat(${rows}, 1fr)`,
marginBottom: '40px',
position: 'relative',
width: '100%',
height: '40vh',
};

if (section?.sectionType === 'Footer') {
randomGrid.push(
<div key={section.sectionType} style={gridStyleThree}>
<div>
<p>{section?.copyright}</p>
<ul>
{section?.links.map((l: string) => (
<li>{l}</li>
))}
</ul>
</div>
</div>
);
return;
}

if (section?.sectionType === 'Testimonials') {
randomGrid.push(
<div key={section.sectionType} style={gridStyleOne}>
<div>
<ul>
{section?.testimonialsSchema?.map((t: any) => {
<li>
<span>{t?.userName}</span>
<span>{t?.review}</span>
</li>;
})}
</ul>
</div>
</div>
);
return;
}

if (section?.sectionType === 'Pricing') {
randomGrid.push(
<div key={section.sectionType} style={gridStyleOne}>
<div>
<ul>
{section?.pricingSchema?.map((t: any) => (
<div>
<li>{t?.plan}</li>
<li>{t?.price}</li>
<li>{t?.heading}</li>
<li>{t?.subheading}</li>
</div>
))}
</ul>
</div>
</div>
);
return;
}

randomGrid.push(
<div key={section.sectionType} style={gridStyleTwo}>
<div>
<h2>{section.heading}</h2>
<h3>{section.subheading}</h3>
<p>{section?.body}</p>
<button>{section.cta}</button>
</div>
</div>
);
});

return randomGrid;
}

This function forms the backbone of my generative content application. It starts by defining fixedSections, such as the header and footer, which are consistently included. Next, dynamicSections are generated based on the provided content.

Afterward, the fixedSections and dynamicSections are combined, ensuring that any repetitive sections are filtered out. This creates a randomGrid, into which the dynamic content will be asynchronously inserted, resulting in a seamless, randomized layout.

Then in my Grid component, I single out this Pricing section and render it dynamically with the content.

if (item.key === 'Pricing') {
return React.cloneElement(item, {
children: (
<div className="grid row-span-full col-span-full">
<div>
<Pricing
pricingItems={
item?.props?.children?.props?.children?.props?.children ||
[]
}
color={colors.color2}
/>
</div>
</div>
),
});
}

I am trying to use React.cloneElement to modify the item element by replacing its children prop with a new set of children that includes a Pricing component.

Here is the Pricing.tsx file, I embed the generative pricing data into my predefined skeleton.

import { useMediaQuery } from 'react-responsive';

const Pricing = ({ pricingItems, color }: any) => {
console.log('pricingItems', pricingItems);
const isMobileOrTable = useMediaQuery({
query: '(max-width: 640px)',
});

const List = ({ item }: any) => {
return (
<div className="max-w-lg">
<li className="text-lg font-medium text-gray-900 dark:text-white">
{item[0]?.props.children}{' '}
</li>
<li className="text-3xl p-8 font-medium text-gray-900 dark:text-white">
$ {item[1]?.props.children} <span className="text-sm">/month</span>{' '}
</li>
<p className="text-lg font-medium text-gray-500 dark:text-white">
{item[2]?.props.children}{' '}
</p>
<p className="text-lg font-medium dark:text-white text-gray-500">
{item[3]?.props.children}{' '}
</p>
<div className="p-8">
<button
className="w-full border-2 p-2"
style={{ borderColor: color }}
>
Buy plan
</button>
</div>
</div>
);
};

return (
<div>
<h2 className="text-3xl font-bold dark:text-white text-center p-12">
Pricing
</h2>
<div
className="grid gap-4 mb-8 rounded-lg w-full"
style={{
gridTemplateColumns: isMobileOrTable
? 'repeat(1, 1fr)'
: `repeat(${pricingItems?.length}, minmax(0, 1fr))`,
}}
>
{pricingItems?.map((c: any, index: number) => {
return (
<div
key={index}
className="flex flex-col items-center justify-center p-12 text-center bg-white w-full dark:bg-gray-800 dark:border-gray-700"
style={{
borderColor: color,
}}
>
<div>
<ul>
<List item={c?.props?.children} />
</ul>
</div>
</div>
);
})}
</div>
</div>
);
};

export default Pricing;

That’s it. That is my approach to making it. Generating content, creating the dynamic grid to structure the entire landing page, and then using this dynamic grid to generate React elements and render them in the DOM.

--

--

Xian Li
Bootcamp

UI/UX designer 👩‍💻 Front-end developer making fun stuff 👩‍💻 Headless e-commerce website builder 👩‍💻