---
title: 'Concepts'
description: 'Learn how Ecopages core works before diving into pages, routing, and integrations.'
order: 1
---

import { RuiAlert, RuiAlertDescription, RuiAlertTitle } from '@ecopages/radiant-ui/alert';

# Understanding Ecopages Core

Before diving into the specifics of writing code for Ecopages, it's important to understand how the core of Ecopages works. This knowledge will help you make better decisions when structuring your project and writing your code.

## Integration System

Ecopages uses a flexible integration system to extend its functionality. Each integration plugin can provide:

1. Template extensions
2. Custom loaders
3. `prepareBuildContributions()` for build-facing declarations

For example, the KitaJS integration might set up template extensions like this:

```typescript
  // html.kita.tsx, 404.kita.tsx, and 500.kita.tsx are discovered automatically
  .setIntegrations([kitajsPlugin()])
```

Those extensions are then used to resolve semantic files such as `html.*`, `404.*`, and `500.*`.

## CSS Processing

By default, Ecopages uses plain css for styling.
We currently provide a simple **Postcss** and **Tailwind (v4 and v3)** plugin that provides a powerful and flexible system for managing your styles via a preset system.
You can customize this in your `eco.config.ts` file:

```typescript
import path from 'node:path';
import { ConfigBuilder } from '@ecopages/core/config-builder';
import { ecopagesJsxPlugin } from '@ecopages/ecopages-jsx';
import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
import { tailwindV4Preset } from '@ecopages/postcss-processor/presets/tailwind-v4';

const appRoot = process.cwd();

const config = await new ConfigBuilder()
	// ... other configurations
	.setProcessors([
		postcssProcessorPlugin(
			tailwindV4Preset({
				referencePath: path.resolve(appRoot, 'src/styles/tailwind.css'),
			}),
		),
	])
	.setIntegrations([ecopagesJsxPlugin()])
	.build();
```

## Script and CSS Loading

Ecopages injects scripts and CSS by walking declared Component Dependencies.

### Discovered and explicit Dependencies

In modules that declare `eco.component()`, `eco.layout()`, `eco.html()`, or `eco.page()`, and in MDX compiled through `@ecopages/mdx/core`:

- Direct local Eco Component imports and relative or aliased side-effect CSS imports are discovered automatically.
- Browser scripts stay explicit in `dependencies.scripts`. Ecopages does **not** detect script files by name pattern.
- Explicit `dependencies.stylesheets` entries override discovered references to the same resolved file (for example `media="print"`). Discovered CSS is not copied into that array.

```tsx
import { BaseLayout } from '@/layouts/base-layout';
import { eco } from '@ecopages/core';
import './home-page.css';

const HomePage = eco.page({
	layout: BaseLayout,
	dependencies: {
		scripts: ['./home-page.script.ts'],
	},
	render: () => (
		<div class="main-content">
			<h1>Welcome to Ecopages</h1>
		</div>
	),
});
```

<RuiAlert variant="info" layout="banner" class="unstyled">
	<RuiAlertTitle>Browser scripts stay explicit</RuiAlertTitle>
	<RuiAlertDescription>
		<p>
			Always list browser scripts in the <code>dependencies.scripts</code> array if you want them processed and
			included in the build. CSS side-effect imports and local Eco Component imports do not need that listing.
		</p>
	</RuiAlertDescription>
</RuiAlert>

### Lazy Dependencies

You can also defer loading of scripts until they are needed using per-entry `dependencies.scripts[].lazy` options. This is useful for improving initial page load performance.

The supported triggers are:

- `on:idle`: Loads when the browser is idle.
- `on:visible`: Loads when the element enters the viewport.
- `on:interaction`: Loads when the user interacts with the element (e.g. mouseover, click, focus).

```typescript
const MyComponent = eco.component({
	dependencies: {
		scripts: [{ src: './heavy-script.ts', lazy: { 'on:visible': true } }], // or provide a specific selector string
	},
	render: () => <div>Heavy Content</div>,
});
```

## Robots.txt and sitemap

Ecopages writes `robots.txt` on every static export from `setRobotsTxt()` preferences.

Optional `sitemap.xml` generation is separate and disabled by default. Enable it with `setSitemap({ enabled: true })` — see [Sitemap](/docs/core/sitemap) for eligibility, `exclude`, `extraUrls`, and page-level `metadata.robots.index`.

## Import CSS as String in JS Files

Ecopages provides a way to load CSS directly into your JavaScript files as strings through Ecopages build plugins. This feature is enabled when you use the `@ecopages/postcss-processor` plugin in your `eco.config.ts`.

## Import MDX as a Component

Ecopages allows you to import MDX files as components. This is useful for creating reusable content and components that can be used across your site.

This capability is provided by the [MDX Integration](/docs/integrations/mdx), which automatically handles MDX transformations.

```typescript
import Component from '@/components/mdx-component';

const MyPage = () => Component();
```

## Project Structure

Ecopages expects a specific project structure:

```

my-project/
├── src/
│ ├── pages/
│ ├── layouts/
│ ├── components/
│ └── includes/
├── public/
├── eco.config.ts
└── package.json

```

- `src/components/`: Stores reusable components (Recommended)
- `src/pages/`: Contains your page files (e.g., `.tsx`, `.mdx`, `.kita.tsx`, `.lit.tsx`) (**Default**)
- `src/layouts/`: Holds layout components (Recommended)
- `src/includes/`: Contains include templates (e.g., `head.tsx`, `html.tsx`, or integration-specific suffixes) (**Default**)
- `public/`: Static assets that will be copied to the build directory (Default)
- `eco.config.ts`: Ecopages configuration file (**Mandatory**)

This structure helps Ecopages efficiently process and build your site. While Ecopages provides these sensible defaults, every directory path is fully customizable via your `eco.config.ts`.

## Runtime Origin and API Requests

When fetching data in `staticPaths` or `staticProps`, Ecopages provides different approaches. **For most cases, calling your data functions directly is recommended** for better performance and reliability.

### Direct Function Calls (Recommended)

For the best performance during static generation, call your data layer functions directly:

```tsx
import { eco } from '@ecopages/core';
import { getAllBlogPosts, getBlogPost } from '@/data/blog';

export default eco.page({
	staticPaths: async () => {
		const posts = getAllBlogPosts();

		return {
			paths: posts.map((post) => ({
				params: { slug: post.slug },
			})),
		};
	},

	staticProps: async ({ pathname }) => {
		const slug = pathname.params.slug;
		const post = getBlogPost(slug);

		if (!post) {
			throw new Error(`Post not found: ${slug}`);
		}

		return { props: { post } };
	},

	render: ({ post }) => <article>{post.title}</article>,
});
```

## Runtime Support

Ecopages supports multiple server adapters. In most guides you will see Bun examples, and Node runtime support is also available through the Node adapter.

This means you'll need to:

- Choose the adapter that matches your runtime and deployment target
- Install dependencies with your preferred package manager (for this repo, `pnpm` is used)
- Ensure your runtime can execute your chosen app entrypoint and scripts

## Server and API Handlers

Ecopages provides a simple and intuitive way to create API endpoints and handle server-side logic. You can find more details in the [Server API](/docs/server/server-api) documentation.

```typescript
import { createApp } from '@ecopages/core/create-app';
import appConfig from './eco.config';

const app = await createApp({ appConfig });

app.get('/api/hello', async () => {
	return new Response(JSON.stringify({ message: 'Hello world!' }));
});

await app.start();
```

## Additional Features

- **Hot Reload**: Ecopages supports hot reloading during development, making the development process more efficient.
- **Static Site Generation**: Ecopages generates static HTML files, improving performance and reducing server load.
- **TypeScript Support**: Ecopages is built with TypeScript and provides excellent TypeScript support out of the box.
