Everything you need to know to build with @plushveil/pages
npm install @plushveil/pages
Search for "Pages" by Plushveil in the VS Code Extensions marketplace, or install via command line:
code --install-extension plushveil.pages
Pages works with .page, .htms, and .htmlfiles.
⚠️ Required:Files must include a <link rel="canonical" href="..." />tag in the <head>section. This defines the output URL. Without it, the file will not be built.
<!DOCTYPE html> <html lang="${lang}"> <head> <meta charset="UTF-8"> <title>${title}</title> <link rel="canonical" href="/" /> </head> <body> <h1>${greeting}</h1> </body> </html> <script target="html"> export const lang = 'en' export const title = 'My Page' export const greeting = 'Hello, World!' </script>
<ul> ${items.map(item => ` <li>${item.name}</li> `).join('')} </ul> <script target="ul"> export const items = [ { name: 'Item 1' }, { name: 'Item 2' }, { name: 'Item 3' } ] </script>
Use arrays in canonical URLs to generate multiple pages from a single source file:
<html> <head> <link rel="canonical" href="/articles/${articleId}" /> </head> <body> <h1>${article.title}</h1> <p>${article.content}</p> </body> </html> <script target="html"> // Array in canonical URL generates multiple pages export const articleId = ['getting-started', 'advanced', 'deployment'] </script> <script target="body"> // Access current page data using page:page import import page from 'page:page' const articles = { 'getting-started': { title: 'Getting Started', content: '...' }, 'advanced': { title: 'Advanced Guide', content: '...' }, 'deployment': { title: 'Deployment', content: '...' } } // Extract current articleId from page URL const currentId = page.url.pathname.split('/').pop() export const article = articles[currentId] </script>
This generates three pages: /articles/getting-started,/articles/advanced, and/articles/deployment
There are two ways to reuse code across pages:
Import HTML snippets directly using template literals:
Create a snippet: snippets/header.html
<header> <h1>My Site</h1> <nav>...</nav> </header>
Import in page:
<body> ${import('./snippets/header.html')} <main>Content</main> </body>
You can pass data to imported snippets using import attributes:
Create snippet with attributes: snippets/card.html
<div class="card"> <h2>${title}</h2> <p>${description}</p> </div> <script target="div"> import args from 'page:args' export const title = args.title || 'Default Title' export const description = args.description || 'No description' </script>
Import with attributes in page:
<body> ${import('./snippets/card.html', { with: { title: 'Hello', description: 'World' } })} ${import('./snippets/card.html', { with: { title: 'Another Card' } })} </body>
Use page:argsimport to access attributes passed via the withoption.
Create reusable components with separate HTML, CSS, and JavaScript that automatically load:
📦 Enable:Add <enable-components></enable-components>in <head>to enable component auto-loading.
1. Create folder structure:
components/ └── my-button/ ├── my-button.html ├── my-button.css └── my-button.ts
2. Component HTML (components/my-button/my-button.html):
<my-button class="btn"> <button>${__attributes.label || 'Click'}</button> </my-button> <script target="my-button"> import page from 'page:page' export const __attributes = page.params.__attributes </script>
3. Use in pages:
<html> <head> <link rel="canonical" href="/" /> <enable-components></enable-components> </head> <body> <my-button label="Submit"></my-button> </body> </html>
How it works:
my-button)<enable-components>page.params.__attributesnpx pages serve <folder> [--config pages.config.mjs]
Starts a development server with hot reload.
npx pages build <folder> [--config pages.config.mjs]
Builds static files to the ./buildfolder.
Create a pages.config.mjsfile:
/** * @file Configuration for pages */ export const baseURI = new URL('https://example.com') export const build = { ignore: [ /\/(node_modules|.git)\//, /\/(components|snippets)\//, ], } export const html = { minify: true, resolve: true } export const css = { minify: true, integrity: true, tailwind: 'tailwind.config.mjs', } export const js = { minify: true, integrity: true, target: '.browserslistrc' }
Use the official GitHub Action to build and deploy your site:
name: Build and Deploy on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest permissions: contents: read pages: write id-token: write steps: - uses: actions/checkout@v4 - id: build name: Build pages uses: plushveil/pages@latest with: folder: pages config: pages.config.mjs - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ${{steps.build.outputs.folder}} - name: Deploy to GitHub Pages uses: actions/deploy-pages@v4
The build action outputs a foldervariable containing the path to the built files. Use ${{steps.build.outputs.folder}}to reference it in subsequent steps.
This is useful for custom deployment workflows, copying files to specific locations, or running post-build scripts.
Use TypeScript in your script blocks:
<script target="html"> interface Item { name: string price: number } export const items: Item[] = [ { name: 'Product 1', price: 29.99 }, { name: 'Product 2', price: 39.99 }, ] export const total: number = items.reduce( (sum, item) => sum + item.price, 0 ) </script>
Use @plushveil/pages in your Node.js scripts:
import { serve } from '@plushveil/pages' const server = await serve('pages/', 'pages.config.mjs') // Server is now running
import { build } from '@plushveil/pages' await build('pages/', 'pages.config.mjs', 'build/') // Pages built to build/ folder
import { render } from '@plushveil/pages' const html = await render('pages/index.page', 'pages.config.mjs') console.log(html)