Build beautiful, print-ready datasheets and PDFs from React templates.
@flanksource/facet is a framework for creating professional datasheets, reports, and documentation using React components. It provides a rich component library optimized for print and PDF generation, along with a powerful CLI for building HTML and PDF outputs.
- 📄 Print-optimized components - 47+ components designed for professional datasheets
- 🎨 React & TypeScript - Full type safety and modern React patterns
- 🔧 Zero-config CLI - Build HTML and PDF with a single command
- 🔗 Component imports -
import { StatCard } from '@flanksource/facet' - ⚡ Fast builds - Powered by Vite with smart caching
- 📦 Isolated builds -
.facet/build directory (like.nextin Next.js)
npm install -g @flanksource/facetDownload platform-specific binaries from GitHub Releases:
- Linux:
facet-linux - macOS:
facet-macos - Windows:
facet-windows.exe
Make executable (Linux/macOS):
chmod +x facet-*
sudo mv facet-* /usr/local/bin/facetCreate a file MyDatasheet.tsx in your project:
import React from 'react';
import {
DatasheetTemplate,
Header,
Page,
StatCard,
Section,
BulletList
} from '@flanksource/facet';
export default function MyDatasheet() {
return (
<DatasheetTemplate>
<Header
title="Mission Control Platform"
subtitle="Cloud-Native Observability & Incident Management"
/>
<Page>
<Section title="Key Metrics">
<div className="grid grid-cols-3 gap-4">
<StatCard label="Response Time" value="< 2min" />
<StatCard label="Uptime" value="99.99%" />
<StatCard label="Incidents Resolved" value="1,247" />
</div>
</Section>
<Section title="Key Features">
<BulletList items={[
'Real-time incident detection and alerting',
'Automated runbook execution',
'Multi-cloud observability',
'Integrated ChatOps workflows'
]} />
</Section>
</Page>
</DatasheetTemplate>
);
}facet html MyDatasheet.tsx -o ./distThis creates:
- Print-ready HTML with embedded styles
- Scoped HTML for embedding in docs (use
--css-scopefor a custom prefix) .facet/- Build cache directory (can be gitignored)
facet pdf MyDatasheet.tsxDatasheetTemplate- Main template wrapper with print stylesPage- Single page containerSection- Section with optional titleHeader- Title header with subtitle and logo supportFooter- Page footer with customizable content
StatCard- Key metric display with label and valueMetricGrid- Grid of metrics with iconsCompactTable- Dense data tablesSpecificationTable- Technical specification tablesComparisonTable- Side-by-side comparisons
BulletList- Styled bullet point listsCalloutBox- Highlighted callout sectionsTwoColumnSection- Two-column layoutsValueProposition- Value prop with icon and descriptionCapabilitySection- Feature capabilities display
IntegrationGrid- Integration logos and descriptionsSocialProof- Customer logos and testimonialsCallToAction- CTA buttons and links
ProgressBar- Progress indicatorsScoreGauge- Circular score gaugesKPITargetActual- Target vs actual KPI displays
SyntaxHighlighter- Syntax-highlighted code blocksTerminalOutput- Terminal-style outputQueryResponseExample- Query/response examples
ProjectSummaryCard- Project summary displaysTaskSummarySection- Task status summariesStageIndicator- Multi-stage progress indicatorsStageSection- Stage-based content sections
SecurityChecksTable- Security check resultsVulnerabilityBreakdown- Vulnerability statisticsSeverityStatCard- Severity-based statsAlertsTable- Alert listings
See full component documentation →
The Page component is the primary layout container for multi-page PDF documents.
<Page
title="Section Title"
product="Mission Control"
header={<Header variant="solid" />}
headerHeight={15}
footer={<PdfFooter />}
footerHeight={15}
margins={{ top: 5, right: 0, bottom: 0, left: 0 }}
watermark="DRAFT"
debug={false}
>
{/* page content */}
</Page>| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
— | Page content |
title |
string |
— | Section title bar text (renders a blue bar below the header) |
product |
string |
— | Sub-label shown in the title bar |
header |
ReactNode |
— | Fixed header rendered at the top of every physical page |
headerHeight |
number (mm) |
0 |
Height of the header; used to offset content so it doesn't overlap |
footer |
ReactNode |
— | Fixed footer rendered at the bottom of every physical page |
footerHeight |
number (mm) |
15 |
Height of the footer; used to add bottom padding to content |
margins |
PageMargins |
{} |
Additional content margins { top, right, bottom, left } in mm |
pageSize |
'a4' |
'a4' |
Page size (A4 only currently) |
watermark |
string |
— | Diagonal watermark text (e.g. "DRAFT", "CONFIDENTIAL") |
debug |
boolean |
false |
Renders dashed red lines at margin boundaries for layout debugging |
className |
string |
— | Extra CSS class applied to the <main> element |
Fixed headers and footers are rendered using position: fixed in Chromium's print engine, which repeats them on every physical page. The @page margins are set to 0 so the header renders flush at the physical top of every page — content spacing is handled entirely by paddingTop/paddingBottom in the Page component.
// Pages with a repeating header
<Page
header={<Header variant="solid" />}
headerHeight={15}
footer={<PdfFooter />}
footerHeight={12}
>
{/* Content starts after 15mm header automatically */}
</Page>
// Cover page — no header, full content area
<Page>
<CoverContent />
</Page>Generate HTML from a React template.
facet html [options] <template>
Options:
--css-scope <prefix> CSS scope prefix for scoped HTML generation
-s, --schema <file> Path to JSON Schema file for data validation
--no-validate Skip data validation
-d, --data <file> Path to JSON data file
-l, --data-loader <file> Path to data loader module (.ts or .js)
-o, --output <path> Output file path or directory (default: "dist")
--output-name-field <field> Data field to use for output filename
-v, --verbose Enable verbose logging
Example:
facet html MyDatasheet.tsx -o ./dist --verbose
facet html MyDatasheet.tsx -d data.json -o report.htmlGenerate PDF from a React template.
facet pdf [options] <template>
Options:
-s, --schema <file> Path to JSON Schema file for data validation
--no-validate Skip data validation
-d, --data <file> Path to JSON data file
-l, --data-loader <file> Path to data loader module (.ts or .js)
-o, --output <path> Output file path or directory (default: "dist")
--output-name-field <field> Data field to use for output filename
-v, --verbose Enable verbose logging
Example:
facet pdf MyDatasheet.tsx -d data.json -o out.pdfStart a preview server with live editing.
facet serve MyDatasheet.tsx -d data.jsonWhen you run facet build MyDatasheet.tsx:
- Setup
.facet/directory - Creates build working directory - Symlink your files - Symlinks your templates and assets into
.facet/src/ - Symlink node_modules - Links dependencies for fast access
- Generate configs - Creates
vite.config.ts,tsconfig.json,entry.tsx - Run Vite build - Compiles React + TypeScript + MDX to static bundle
- Server-side render - Renders React to static HTML
- Output files - Writes HTML to your
dist/directory
your-project/
├── MyDatasheet.tsx # Your template
├── node_modules/
│ └── @facet/ # Installed package
├── .facet/ # Build cache (auto-generated)
│ ├── src/ # Symlinks to your files
│ │ └── MyDatasheet.tsx -> ../../MyDatasheet.tsx
│ ├── node_modules/ # Symlink to ../node_modules
│ ├── entry.tsx # Generated entry point
│ ├── vite.config.ts # Generated Vite config
│ └── dist/ # Vite build output
└── dist/ # Final output
├── datasheet-MyDatasheet.html
└── datasheet-MyDatasheet-scoped.html
Similar to Next.js (.next/) or Nuxt (.nuxt/), the .facet/ directory:
- Isolates build artifacts - Keeps your project clean
- Enables fast rebuilds - Symlinks avoid file copying
- Supports incremental builds - Only rebuilds what changed
- Simplifies debugging - All build files in one place
Add to .gitignore:
.facet/
dist/import { StatCard, Header, Page } from '@flanksource/facet';All components include full TypeScript definitions:
import { StatCard } from '@flanksource/facet';
<StatCard
label="Response Time" // string
value="< 2min" // string | number
trend="up" // 'up' | 'down' | 'neutral' (optional)
icon="clock" // string (optional)
/>Components use Tailwind CSS for styling. Include Tailwind in your project:
npm install -D tailwindcss autoprefixer postcsstailwind.config.js:
module.exports = {
content: [
'./MyDatasheet.tsx',
'./node_modules/@facet/core/src/components/**/*.tsx'
],
theme: {
extend: {},
},
plugins: [],
}Templates support MDX for content-rich pages:
// MyDatasheet.tsx
import Content from './content.mdx';
export default function MyDatasheet() {
return (
<DatasheetTemplate>
<Content />
</DatasheetTemplate>
);
}# content.mdx
## Overview
This is **MDX content** with React components:
<StatCard label="Users" value="10,000+" />
- Bullet point one
- Bullet point twoUse Storybook for component development:
npm run storybooknpm run build:clinpm run prepublishOnly # Builds CLI automatically
npm publishsrc/components/- React component library (47 components)src/styles.css- Global styles and Tailwindcli/- CLI package sourcecli/src/builders/- Build orchestrationcli/src/generators/- HTML/PDF generatorscli/src/utils/- Shared utilitiescli/src/plugins/- Vite plugins
assets/- Static assets (logos, icons)
See src/examples/ for complete working examples:
- Basic Datasheet - Simple single-page datasheet
- Multi-page Report - Complex multi-page document
- Security Report - Security-focused datasheet
- POC Evaluation - POC evaluation template
This is an internal Flanksource package. For issues or feature requests, contact the platform team.
Proprietary - Flanksource Inc.
Built with ❤️ by Flanksource