Installation
Install the Harpy CLI or integrate Harpy.js manually into an existing project. This page covers system requirements, quick start using the CLI, manual installation, detailed setup steps, and the recommended project structure.
Quick tip: The CLI is the fastest path to a working project. Use manual install only if you need to integrate Harpy.js into an existing codebase.
System Requirements
Minimum
- Node.js: 18+ (20.x recommended for latest features) — native ESM & modern runtime APIs
- Package manager: pnpm (recommended), npm, or yarn
- Git installed
Installation Methods
Choose the method that fits your workflow. The CLI scaffolds a complete project; manual installation is for integrating into existing projects.
Quick Start (recommended)
Use the Harpy CLI to create a new project with sensible defaults and a working dev server.
pnpm i -g @hepta-solutions/harpy-cliharpy create my-appcd my-apppnpm dev
The CLI will scaffold routes, layouts, a working dev server and example pages so you can focus on building features.
Manual Installation
Install the core packages and configure the rendering engine if you prefer to add Harpy.js to an existing project.
pnpm add @hepta-solutions/harpy-core react react-dom
You'll then set up a layout, configure rendering in your server entry, and add decorators to your controllers (if using NestJS or similar) to render JSX pages.
Why JSX/layouts? Harpy.js uses JSX-based layouts to enable full server-side rendering plus client hydration — layouts are components that wrap pages. Ensure your environment supports JSX tooling.
Detailed Steps
1. Install Dependencies
Add the core runtime and React (latest version recommended) to your project.
pnpm add @hepta-solutions/harpy-core react react-dom2. Create a Layout Component
Layouts render the common shell around pages (head tags, header, footer, navigation) and receive metadata and hydration script definitions.
// layouts/layout.tsx
import * as React from 'react';
import { JsxLayoutProps } from '@hepta-solutions/harpy-core';
export default function MainLayout({ children, meta, hydrationScripts, sections, lang }: JsxLayoutProps & { lang?: string }) {
const title = meta?.title ?? 'Harpy Framework';
const description = meta?.description;
const canonical = meta?.canonical;
const og = meta?.openGraph ?? {};
const twitter = meta?.twitter ?? {};
const chunkScripts = hydrationScripts || [];
return (
<html lang={lang || 'en'}>
<head>
<title>{title}</title>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/styles.css" /> // 👈 Makes sure to add this for styling
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{/* Open Graph tags */}
<meta property="og:title" content={og.title || title} />
<meta
property="og:description"
content={og.description || description}
/>
<meta property="og:type" content={og.type || 'website'} />
{og.image && <meta property="og:image" content={og.image} />}
{og.url && <meta property="og:url" content={og.url} />}
{/* Twitter cards */}
<meta
name="twitter:card"
content={twitter.card || 'summary_large_image'}
/>
<meta name="twitter:title" content={twitter.title || title} />
<meta
name="twitter:description"
content={twitter.description || description}
/>
{twitter.image && <meta name="twitter:image" content={twitter.image} />}
<link rel="stylesheet" href="/styles/styles.css" />
</head>
<body>
<aside>
<nav className="px-6 py-6 space-y-8">
{sections.map((section) => (
<div key={section.id}>
<h3 >
{section.title}
</h3>
<ul className="space-y-1">
{section.items.map((item) => (
<li key={item.id}>
<a href={item.href} >
{item.title}
</a>
</li>
))}
</ul>
</div>
))}
</nav>
</aside>
<main>
{children}
<main>
{/* Auto-injected hydration scripts */}
{chunkScripts.map((script) => (
<script key={script.componentName} src={script.path}></script>
))}
</body>
</html>
);
}3. Configure the Server Entry
Register the JSX engine and static serving in your server bootstrap. Example below shows the renderer being wired into an Fastify server.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { withJsxEngine } from '@hepta-solutions/harpy-core';
import fastifyStatic from '@fastify/static';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import DefaultLayout from './layouts/layout';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
// Set up JSX rendering engine
withJsxEngine(app, DefaultLayout);
// Register Fastify plugins
const fastify = app.getHttpAdapter().getInstance();
// Register static file serving
await fastify.register(fastifyStatic, {
root: path.join(process.cwd(), 'dist'),
prefix: '/',
decorateReply: false,
});
await app.listen(3000);
}
bootstrap();4. Add Decorators / Render Hooks
If you use a controller-based server (NestJS or similar), use the provided decorator to render JSX pages from route handlers. Otherwise, call the renderer directly from your route handlers.
// controller example (conceptual)
@Controller()
export class HomeController {
constructor(private readonly navigationService: NavigationService) {}
@Get()
@JsxRender(Page, {
layout: CustomLayout, // 👈 Provide a specific layout here, otherwise DefaultLayout will be used
meta: {
title: 'your page title',
description:
'your page description',
canonical: 'https://your-site.com/your-page',
openGraph: {
title: 'OPen graph title',
description:
'Open graph description',
type: 'website',
url: 'https://your-site.com/your-page',
},
// Other meta data...
},
})
async home(@CurrentLocale() locale: string): Promise<PageProps> {
const dict = await getDictionary(locale); // 👈 Load translations
const sections = this.navigationService.getAllSections(); // 👈 Load navigation sections
return {
sections,
dict,
locale,
};
}
}5. Start the Dev Server
Run the dev server and open your app in the browser:
pnpm dev
# or
npm run devThe dev server typically includes hot reload for server code and fast client-side updates for components.
Recommended Project Structure
This layout-agnostic structure helps the Harpy rendering engine discover pages, layouts and static assets.
my-app/
├─ src/
│ ├─ assets/ # static assets (imust include styles.css)
│ ├─ components/ # reusable UI components
│ ├─ layouts/ # layout components (main, dashboard, etc.)
│ ├─ dictionaries/ # translation dictionaries
│ ├─ features/ # feature modules (auth, dashboard, etc.)
│ ├─ i18n/ # internationalization config and utilities
│ ├─ shared/ # shared module (must include navigation module)
│ ├─ app.module.ts # root module
│ ├─ main.ts # server bootstrap
├─ public/ # static assets
├─ package.json
└─ tailwind.config.ts # framework configuration (Tailwind CSS)- src/assets — Contains static assets such as images, fonts, and required global files like
styles.css. - src/components — Reusable UI components shared across the application.
- src/layouts — Layout components (e.g., main layout, dashboard layout) that wrap feature pages.
- src/dictionaries — Translation dictionaries used for multi-language support.
- src/features — Feature-based modules (auth, dashboard, admin, etc.), each grouping pages, hooks, and logic.
- src/i18n — Internationalization configuration and utilities.
- src/shared — Shared module containing utilities, adapters, and the required navigation module.
- src/app.module.ts — The root application module.
- src/main.ts — The main server bootstrap, typically registering the Fastify adapter and SSR engine.
- public/ — Static public assets.
- tailwind.config.ts — Tailwind CSS framework configuration.