Skip to content

Project Structure

Understanding how a BlocksWeb project is organized will help you navigate your codebase and know where to place different types of files.

Standard Project Structure

Here's what a typical BlocksWeb project looks like:

my-blocksweb-project/
├── app/                          # Next.js App Router (or pages/)
│   ├── [[...path]]/              # Catch-all route for BlocksWeb pages
│   │   └── page.tsx              # Main page component
│   ├── layout.tsx                # Root layout
│   └── globals.css               # Global styles
├── components/                   # Your BlocksWeb components
│   ├── Hero.tsx
│   ├── FeatureCard.tsx
│   ├── BlogPost.tsx
│   └── ProductGrid.tsx
├── public/                       # Static assets
│   ├── images/
│   ├── favicon.ico
│   └── robots.txt
├── lib/                          # Utility functions
│   ├── utils.ts
│   └── api.ts
├── types/                        # TypeScript type definitions
│   └── index.ts
├── blocksweb.config.ts           # BlocksWeb configuration
├── next.config.js                # Next.js configuration
├── tailwind.config.js            # Tailwind configuration (if using)
├── tsconfig.json                 # TypeScript configuration
├── .env.local                    # Environment variables (not committed)
├── .gitignore
├── package.json
└── README.md

Key Files and Folders

blocksweb.config.ts

The main BlocksWeb configuration file. This is where you:

  • Register components
  • Define collections
  • Configure settings
  • Add scripts and styles
typescript
import { IBlockswebComponent } from '@blocksweb/core';
import Hero from './components/Hero';
import FeatureCard from './components/FeatureCard';

export const editorComponents: IBlockswebComponent[] = [
  Hero,
  FeatureCard,
];

export const collections = [
  // Define your collections here
];

export const settings = {
  editorComponents,
  collections,
  scripts: ['https://cdn.tailwindcss.com'],
  styles: ['/custom-styles.css'],
};

app/[[...path]]/page.tsx (App Router)

The catch-all route that renders BlocksWeb pages:

typescript
import { BlockswebProvider, Canvas } from '@blocksweb/core/client';
import { settings } from '@/blocksweb.config';

export default function Page({ params }: { params: { path?: string[] } }) {
  const path = params.path ? '/' + params.path.join('/') : '/';

  return (
    <BlockswebProvider settings={settings}>
      <Canvas path={path} />
    </BlockswebProvider>
  );
}

components/ Directory

Store all your BlocksWeb components here. Organize them by category if needed:

components/
├── marketing/
│   ├── Hero.tsx
│   ├── CallToAction.tsx
│   └── Testimonials.tsx
├── content/
│   ├── RichContent.tsx
│   ├── ImageGallery.tsx
│   └── VideoEmbed.tsx
├── layout/
│   ├── Header.tsx
│   ├── Footer.tsx
│   └── Container.tsx
└── ecommerce/
    ├── ProductCard.tsx
    ├── ProductGrid.tsx
    └── AddToCart.tsx

.env.local

Environment variables for development. Never commit this file!

bash
# BlocksWeb Configuration
BLOCKSWEB_API_KEY=bw_live_...
BLOCKSWEB_WORKSPACE_ID=ws_...

# Optional: Self-hosted API
BLOCKSWEB_API_URL=https://api.yoursite.com

# Optional: Feature flags
BLOCKSWEB_ENABLE_ANALYTICS=true
BLOCKSWEB_ENABLE_AB_TESTING=true

lib/ Directory

Utility functions and helpers:

typescript
// lib/utils.ts
export function formatDate(date: Date): string {
  return new Intl.DateTimeFormat('en-US').format(date);
}

export function cn(...classes: string[]): string {
  return classes.filter(Boolean).join(' ');
}

types/ Directory

Shared TypeScript types:

typescript
// types/index.ts
export type Product = {
  id: string;
  name: string;
  price: number;
  image: string;
  description: string;
};

export type BlogPost = {
  id: string;
  title: string;
  author: string;
  date: Date;
  content: string;
  slug: string;
};

App Router vs Pages Router

BlocksWeb supports both Next.js routing systems.

app/
├── [[...path]]/
│   └── page.tsx              # BlocksWeb pages
├── api/
│   └── custom-endpoint/
│       └── route.ts          # API routes
├── layout.tsx                # Root layout
├── not-found.tsx             # 404 page
└── globals.css

Pages Router Structure

pages/
├── [[...path]].tsx           # BlocksWeb pages
├── api/
│   └── custom-endpoint.ts    # API routes
├── _app.tsx                  # App wrapper
├── _document.tsx             # Document
└── 404.tsx                   # 404 page

styles/
└── globals.css

Advanced Organization

For larger projects, consider this structure:

my-blocksweb-project/
├── apps/                     # Monorepo apps (optional)
│   └── web/
├── packages/                 # Shared packages (optional)
│   └── ui/
├── src/
│   ├── components/
│   │   ├── blocksweb/        # BlocksWeb components
│   │   │   ├── marketing/
│   │   │   ├── content/
│   │   │   └── ecommerce/
│   │   └── ui/               # Regular UI components
│   │       ├── Button.tsx
│   │       └── Card.tsx
│   ├── lib/
│   │   ├── blocksweb/
│   │   │   ├── collections/  # Collection definitions
│   │   │   └── providers/    # Custom providers
│   │   └── utils/
│   ├── hooks/                # Custom React hooks
│   │   ├── useProducts.ts
│   │   └── useBlogPosts.ts
│   ├── types/
│   └── config/
│       └── blocksweb.config.ts
├── public/
├── .env.local
└── package.json

Component File Structure

Each component file should follow this pattern:

typescript
// 1. Imports
import { IBlockswebComponent } from '@blocksweb/core';
import { RichText, Image } from '@blocksweb/core/client';

// 2. Type definitions
type MyComponentProps = {
  title: string;
  description: string;
};

// 3. Component implementation
const MyComponent: IBlockswebComponent<MyComponentProps> = ({
  title,
  description
}) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>{description}</p>
    </div>
  );
};

// 4. Schema definition
MyComponent.schema = {
  displayName: 'My Component',
  category: 'Content',
  options: [
    { type: 'text', name: 'title', label: 'Title', default: '' },
    { type: 'text', name: 'description', label: 'Description', default: '' },
  ],
};

// 5. Export
export default MyComponent;

Environment Files

Use different environment files for different stages:

.env.local          # Local development (not committed)
.env.development    # Development defaults (can be committed)
.env.production     # Production values (not committed)
.env.example        # Example file (committed)

Example .env.example:

bash
# BlocksWeb Configuration
BLOCKSWEB_API_KEY=your_key_here
BLOCKSWEB_WORKSPACE_ID=your_workspace_id_here

# Optional Configuration
BLOCKSWEB_API_URL=https://cloud.blocksweb.nl
BLOCKSWEB_ENABLE_ANALYTICS=true

gitignore

Make sure your .gitignore includes:

gitignore
# dependencies
node_modules/
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.production.local
.env.development.local
.env.test.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

Package.json Scripts

Useful scripts for your package.json:

json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "type-check": "tsc --noEmit",
    "format": "prettier --write \"**/*.{ts,tsx,md}\"",
    "clean": "rm -rf .next node_modules"
  }
}

Collections Organization

If using collections, organize them in a dedicated folder:

lib/blocksweb/collections/
├── index.ts                  # Export all collections
├── products.ts               # Product collection
├── blogPosts.ts              # Blog post collection
└── providers/                # Custom providers
    ├── productProvider.ts
    └── blogProvider.ts

Example collections/index.ts:

typescript
import { products } from './products';
import { blogPosts } from './blogPosts';

export const collections = [products, blogPosts];

Best Practices

1. Consistent Naming

typescript
// Component files: PascalCase
Hero.tsx
FeatureCard.tsx
ProductGrid.tsx

// Utility files: camelCase
formatDate.ts
fetchProducts.ts
validateInput.ts

// Type files: PascalCase types
types/Product.ts
types/BlogPost.ts

2. One Component Per File

typescript
// Good
components/Hero.tsx
components/FeatureCard.tsx

// Avoid
components/AllComponents.tsx  // ❌ Multiple components in one file
components/Hero/
├── Hero.tsx
├── Hero.test.tsx
├── Hero.stories.tsx
└── Hero.module.css

4. Use Index Files for Clean Imports

typescript
// components/index.ts
export { default as Hero } from './Hero';
export { default as FeatureCard } from './FeatureCard';
export { default as ProductGrid } from './ProductGrid';

// Then import like this:
import { Hero, FeatureCard } from '@/components';

Monorepo Structure (Advanced)

For large organizations, use a monorepo:

my-blocksweb-monorepo/
├── apps/
│   ├── website/              # Main website
│   ├── blog/                 # Separate blog app
│   └── admin/                # Admin dashboard
├── packages/
│   ├── blocksweb-components/ # Shared BlocksWeb components
│   ├── ui/                   # Shared UI components
│   ├── types/                # Shared types
│   └── utils/                # Shared utilities
├── package.json              # Root package.json
├── turbo.json                # Turborepo config
└── pnpm-workspace.yaml       # PNPM workspace config

Next Steps

Now that you understand the project structure:

Reference