import React, { useState, useRef } from ‘react’;
import { Download, Copy, Trash2, Move, Plus, Type, Image, Palette, Layout } from ‘lucide-react’;
const App = () => {
const [blocks, setBlocks] = useState([
{
id: ‘header-1’,
type: ‘header’,
content: {
title: ‘Welcome to Your Amazing Site’,
subtitle: ‘Create stunning landing pages in minutes’,
ctaText: ‘Get Started’,
backgroundImage: ‘https://placehold.co/1200×600/4f46e5/white?text=Header+Background’
},
styles: {
backgroundColor: ‘bg-indigo-900’,
textColor: ‘text-white’,
padding: ‘py-20’
}
},
{
id: ‘features-1’,
type: ‘features’,
content: {
title: ‘Why Choose Us’,
features: [
{ title: ‘Easy to Use’, description: ‘No coding required, just drag and drop’ },
{ title: ‘Responsive Design’, description: ‘Looks great on all devices’ },
{ title: ‘Free Forever’, description: ‘No hidden costs or subscriptions’ }
]
},
styles: {
backgroundColor: ‘bg-white’,
textColor: ‘text-gray-800’,
padding: ‘py-16’
}
},
{
id: ‘text-1’,
type: ‘text’,
content: {
title: ‘About Our Service’,
text: ‘Our platform empowers you to create beautiful, professional landing pages without any technical knowledge. Simply customize the content, adjust the styling, and download your ready-to-publish HTML file.’
},
styles: {
backgroundColor: ‘bg-gray-50’,
textColor: ‘text-gray-700’,
padding: ‘py-12’
}
},
{
id: ‘gallery-1’,
type: ‘gallery’,
content: {
title: ‘Our Work’,
images: [
‘https://placehold.co/400×300/6366f1/white?text=Project+1’,
‘https://placehold.co/400×300/8b5cf6/white?text=Project+2’,
‘https://placehold.co/400×300/ec4899/white?text=Project+3’
]
},
styles: {
backgroundColor: ‘bg-white’,
textColor: ‘text-gray-800’,
padding: ‘py-16’
}
}
]);
const [selectedBlock, setSelectedBlock] = useState(null);
const [editingField, setEditingField] = useState(null);
const fileInputRef = useRef(null);
const blockTypes = [
{ type: ‘header’, name: ‘Header’, icon: Layout },
{ type: ‘features’, name: ‘Features’, icon: Type },
{ type: ‘text’, name: ‘Text Block’, icon: Type },
{ type: ‘gallery’, name: ‘Gallery’, icon: Image }
];
const addBlock = (type) => {
const newBlock = {
id: `${type}-${Date.now()}`,
type,
content: getDefaultContent(type),
styles: {
backgroundColor: ‘bg-white’,
textColor: ‘text-gray-800’,
padding: ‘py-12’
}
};
setBlocks([…blocks, newBlock]);
};
const getDefaultContent = (type) => {
switch (type) {
case ‘header’:
return {
title: ‘New Header Title’,
subtitle: ‘Header subtitle’,
ctaText: ‘Button Text’,
backgroundImage: ‘https://placehold.co/1200×600/6366f1/white?text=Header’
};
case ‘features’:
return {
title: ‘New Features Section’,
features: [
{ title: ‘Feature 1’, description: ‘Description for feature 1’ },
{ title: ‘Feature 2’, description: ‘Description for feature 2’ }
]
};
case ‘text’:
return {
title: ‘New Text Section’,
text: ‘This is your new text content. Edit this to your liking.’
};
case ‘gallery’:
return {
title: ‘New Gallery’,
images: [
‘https://placehold.co/400×300/6366f1/white?text=Image+1’,
‘https://placehold.co/400×300/8b5cf6/white?text=Image+2’
]
};
default:
return {};
}
};
const updateBlockContent = (blockId, field, value) => {
setBlocks(blocks.map(block =>
block.id === blockId
? { …block, content: { …block.content, [field]: value } }
: block
));
};
const updateBlockStyle = (blockId, style, value) => {
setBlocks(blocks.map(block =>
block.id === blockId
? { …block, styles: { …block.styles, [style]: value } }
: block
));
};
const moveBlock = (fromIndex, toIndex) => {
const newBlocks = […blocks];
const [movedBlock] = newBlocks.splice(fromIndex, 1);
newBlocks.splice(toIndex, 0, movedBlock);
setBlocks(newBlocks);
};
const deleteBlock = (blockId) => {
setBlocks(blocks.filter(block => block.id !== blockId));
if (selectedBlock === blockId) setSelectedBlock(null);
};
const duplicateBlock = (blockId) => {
const blockToDuplicate = blocks.find(block => block.id === blockId);
const newBlock = {
…blockToDuplicate,
id: `${blockToDuplicate.type}-${Date.now()}`,
content: JSON.parse(JSON.stringify(blockToDuplicate.content))
};
setBlocks([…blocks, newBlock]);
};
const generateHTML = () => {
const htmlContent = `
My Custom Landing Page
${blocks.map(block => renderBlockHTML(block)).join(»)}
`;
const blob = new Blob([htmlContent], { type: ‘text/html’ });
const url = URL.createObjectURL(blob);
const a = document.createElement(‘a’);
a.href = url;
a.download = ‘index.html’;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const renderBlockHTML = (block) => {
const { type, content, styles } = block;
switch (type) {
case ‘header’:
return `
${content.title}
${content.subtitle}
${content.ctaText}
`;
case ‘features’:
return `
${content.title}
${content.features.map(feature => `
${feature.title}
${feature.description}
`).join(»)}
`;
case ‘text’:
return `
${content.title}
${content.text}
`;
case ‘gallery’:
return `
${content.title}
${content.images.map(image => `
`).join(»)}
`;
default:
return »;
}
};
const renderBlock = (block, index) => {
const isSelected = selectedBlock === block.id;
const { type, content, styles } = block;
return (
setSelectedBlock(block.id)}
>
{isSelected && (
{ e.stopPropagation(); duplicateBlock(block.id); }}
className=»bg-blue-500 text-white p-1 rounded hover:bg-blue-600″
title=»Duplicate»
>
{ e.stopPropagation(); deleteBlock(block.id); }}
className=»bg-red-500 text-white p-1 rounded hover:bg-red-600″
title=»Delete»
>
)}
{type === ‘header’ && (
)}
{type === ‘features’ && (
)}
{type === ‘text’ && (
)}
{type === ‘gallery’ && (
)}
);
};
return (
{/* Header */}
Landing Page Builder
Download HTML
{/* Sidebar */}
Add New Block
{blockTypes.map((blockType) => (
addBlock(blockType.type)}
className=»w-full flex items-center gap-3 p-3 bg-gray-50 hover:bg-gray-100 rounded-lg transition»
>
{blockType.name}
))}
{selectedBlock && (
Block Settings
Background
b.id === selectedBlock)?.styles.backgroundColor || ‘bg-white’}
onChange={(e) => updateBlockStyle(selectedBlock, ‘backgroundColor’, e.target.value)}
className=»w-full p-2 border rounded»
>
White
Light Gray
Dark Indigo
Dark Gray
Text Color
b.id === selectedBlock)?.styles.textColor || ‘text-gray-800’}
onChange={(e) => updateBlockStyle(selectedBlock, ‘textColor’, e.target.value)}
className=»w-full p-2 border rounded»
>
Dark
White
Indigo
Padding
b.id === selectedBlock)?.styles.padding || ‘py-12’}
onChange={(e) => updateBlockStyle(selectedBlock, ‘padding’, e.target.value)}
className=»w-full p-2 border rounded»
>
Small
Medium
Large
Extra Large
)}
{/* Main Content */}
Tip: Click on any text to edit it. Click on a block to select it and access styling options in the sidebar.
Drag blocks to reorder them (use the drag handle on selected blocks).
{blocks.map((block, index) => (
{renderBlock(block, index)}
))}
{blocks.length === 0 && (
No blocks added yet. Add your first block from the sidebar!
)}
);
};
export default App;