Your First Component
In this tutorial, you'll build a complete Blocksweb component from scratch. We'll create a feature card component that can be used on marketing pages.
What You'll Build
By the end of this tutorial, you'll have created a reusable "Feature Card" component that includes:
- An icon or image
- A title
- A description
- An optional call-to-action button
Prerequisites
Before starting, make sure you have:
- A Blocksweb project set up (Installation Guide)
- Basic knowledge of React and TypeScript
- Your development server running (
npm run dev)
Step 1: Create the Component File
Create a new file components/FeatureCard.tsx:
mkdir -p components
touch components/FeatureCard.tsxStep 2: Define the TypeScript Interface
Start by defining the props your component will receive:
// components/FeatureCard.tsx
import { IBlockswebComponent } from "@blocksweb/core";
type FeatureCardProps = {
icon: string;
title: string;
description: string;
buttonText?: string;
buttonUrl?: string;
showButton: boolean;
};TIP
Adding type definitions helps with autocomplete and catches errors early.
Step 3: Create the Component
Now create the React component:
const FeatureCard: IBlockswebComponent<FeatureCardProps> = ({
icon,
title,
description,
buttonText,
buttonUrl,
showButton
}) => {
return (
<div className="feature-card bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
{/* Icon */}
<div className="icon-wrapper mb-4">
<img
src={icon}
alt={title}
className="w-16 h-16 object-contain"
/>
</div>
{/* Title */}
<h3 className="text-2xl font-bold mb-3 text-gray-800">
{title}
</h3>
{/* Description */}
<p className="text-gray-600 mb-4 leading-relaxed">
{description}
</p>
{/* Button (conditional) */}
{showButton && buttonText && buttonUrl && (
<a
href={buttonUrl}
className="inline-block bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 transition-colors"
>
{buttonText}
</a>
)}
</div>
);
};Step 4: Add the Schema
This is where the Blocksweb magic happens. The schema defines what properties can be edited in the visual editor:
FeatureCard.schema = {
displayName: "Feature Card",
category: "Marketing",
options: [
{
type: "image",
name: "icon",
label: "Icon/Image",
default: "https://placehold.co/64x64",
},
{
type: "text",
name: "title",
label: "Title",
default: "Amazing Feature",
},
{
type: "text",
name: "description",
label: "Description",
default:
"This feature will revolutionize your workflow and make your life easier.",
},
{
type: "checkbox",
name: "showButton",
label: "Show Button",
default: true,
},
{
type: "text",
name: "buttonText",
label: "Button Text",
default: "Learn More",
},
{
type: "text",
name: "buttonUrl",
label: "Button URL",
default: "#",
},
],
};Schema Breakdown
Let's understand each part:
- displayName: How the component appears in the component list
- category: Groups components together (e.g., "Marketing", "Content", "Layout")
- options: Array of editable properties
Each option has:
- type: The input type (text, image, checkbox, etc.)
- name: The prop name passed to your component
- label: What users see in the editor
- default: Initial value
Step 5: Export the Component
Add this at the end of your file:
export default FeatureCard;Complete File
Here's the complete FeatureCard.tsx:
import { IBlockswebComponent } from '@blocksweb/core';
type FeatureCardProps = {
icon: string;
title: string;
description: string;
buttonText?: string;
buttonUrl?: string;
showButton: boolean;
};
const FeatureCard: IBlockswebComponent<FeatureCardProps> = ({
icon,
title,
description,
buttonText,
buttonUrl,
showButton
}) => {
return (
<div className="feature-card bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<div className="icon-wrapper mb-4">
<img src={icon} alt={title} className="w-16 h-16 object-contain" />
</div>
<h3 className="text-2xl font-bold mb-3 text-gray-800">{title}</h3>
<p className="text-gray-600 mb-4 leading-relaxed">{description}</p>
{showButton && buttonText && buttonUrl && (
<a
href={buttonUrl}
className="inline-block bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 transition-colors"
>
{buttonText}
</a>
)}
</div>
);
};
FeatureCard.schema = {
displayName: 'Feature Card',
category: 'Marketing',
options: [
{
type: 'image',
name: 'icon',
label: 'Icon/Image',
default: 'https://placehold.co/64x64'
},
{
type: 'text',
name: 'title',
label: 'Title',
default: 'Amazing Feature'
},
{
type: 'text',
name: 'description',
label: 'Description',
default: 'This feature will revolutionize your workflow.'
},
{
type: 'checkbox',
name: 'showButton',
label: 'Show Button',
default: true
},
{
type: 'text',
name: 'buttonText',
label: 'Button Text',
default: 'Learn More'
},
{
type: 'text',
name: 'buttonUrl',
label: 'Button URL',
default: '#'
}
]
};
export default FeatureCard;Step 6: Register the Component
Open blocksweb.config.ts and import your component:
import { IBlockswebComponent } from "@blocksweb/core";
import FeatureCard from "./components/FeatureCard";
export const editorComponents: IBlockswebComponent[] = [
FeatureCard,
// Add more components here
];
export const settings = {
editorComponents,
scripts: ["https://cdn.tailwindcss.com"],
};Complete Configuration Guide
See the Blocksweb Configuration guide for detailed information about registering components, collections, and configuring your application.
Step 7: Test in the Editor
- Make sure your dev server is running (
npm run dev) - Open http://localhost:3000
- Sign in to the editor
- Click "+ Add Block"
- Find "Feature Card" under the "Marketing" category
- Click to add it to the page
You should see your Feature Card component appear!
Step 8: Customize Properties
With the component selected:
- Click on the Icon/Image field
- Upload an icon or enter an image URL
- Change the Title to something like "Lightning Fast"
- Update the Description
- Toggle Show Button on/off
- Modify the Button Text and Button URL
Watch as your changes appear in real-time!
Enhancements
Let's make our component even better.
Add Hover Effects
<div className="feature-card bg-white rounded-lg shadow-md p-6 hover:shadow-lg hover:scale-105 transition-all duration-300">
{/* ... */}
</div>Add Icon Styles
Allow users to choose icon styles:
type FeatureCardProps = {
// ... existing props
iconStyle: 'circle' | 'square' | 'none';
};
// In the component:
const iconClasses = `w-16 h-16 object-contain ${
iconStyle === 'circle' ? 'rounded-full bg-blue-100 p-3' :
iconStyle === 'square' ? 'rounded-lg bg-gray-100 p-3' :
''
}`;
// In the schema:
{
type: 'select',
name: 'iconStyle',
label: 'Icon Style',
options: [
{ label: 'Circle', value: 'circle' },
{ label: 'Square', value: 'square' },
{ label: 'None', value: 'none' }
],
default: 'circle'
}Add Rich Text Description
Replace the plain text description with rich text:
import { RichText } from '@blocksweb/core/client';
// In the component:
<RichText
propName="description"
text={description}
defaultText="This feature will revolutionize your workflow."
/>
// In the schema (change type):
{
type: 'richtext',
name: 'description',
label: 'Description',
default: 'This feature will revolutionize your workflow.'
}Best Practices
1. Use Semantic HTML
// Good
<article className="feature-card">
<figure><img src={icon} alt={title} /></figure>
<h3>{title}</h3>
<p>{description}</p>
</article>
// Avoid
<div className="feature-card">
<div><img src={icon} alt={title} /></div>
<div>{title}</div>
<div>{description}</div>
</div>2. Provide Good Defaults
Always set sensible defaults so the component looks good immediately after being added.
3. Add Alt Text
Always include alt text for images for accessibility:
<img src={icon} alt={`${title} icon`} />4. Make It Responsive
Use responsive Tailwind classes:
<div className="feature-card p-4 md:p-6 lg:p-8">
<h3 className="text-xl md:text-2xl lg:text-3xl">
{title}
</h3>
</div>5. Group Related Options
Use clear labels and logical ordering in your schema:
options: [
// Content
{ type: "image", name: "icon", label: "Icon" },
{ type: "text", name: "title", label: "Title" },
{ type: "richtext", name: "description", label: "Description" },
// Button
{ type: "checkbox", name: "showButton", label: "Show Button" },
{ type: "text", name: "buttonText", label: "Button Text" },
{ type: "text", name: "buttonUrl", label: "Button URL" },
// Styling
{ type: "select", name: "iconStyle", label: "Icon Style" },
{ type: "color", name: "backgroundColor", label: "Background Color" },
];Common Patterns
Conditional Rendering
{showButton && (
<button>{buttonText}</button>
)}Dynamic Styling
<div
className="feature-card"
style={{ backgroundColor: bgColor }}
>Mapping Arrays
{features.map((feature, index) => (
<li key={index}>{feature}</li>
))}Next Steps
Congratulations! You've created your first Blocksweb component. Now explore:
- Options Reference - Learn about all 12 option types
- Creating Components - Advanced component patterns
- Utilities - Use RichText, Image, and BlockOutlet
- Data Flow - Understand how props flow from editor
Troubleshooting
Component Not Showing Up
- Check it's exported:
export default FeatureCard - Check it's registered in
blocksweb.config.ts - Check the schema is attached:
FeatureCard.schema = { ... } - Refresh the browser
TypeScript Errors
Make sure your types match:
// Props type should match schema options
type FeatureCardProps = {
icon: string; // matches { type: 'image', name: 'icon' }
title: string; // matches { type: 'text', name: 'title' }
showButton: boolean; // matches { type: 'checkbox', name: 'showButton' }
};Styling Not Working
If using Tailwind:
- Check Tailwind is in
blocksweb.config.tsscripts - Or import Tailwind CSS in your root layout
- Make sure classes are not conflicting
You're now ready to build more complex components!