Configuration

This section details all the supported configuration options available with Greenwood, which you can define in a greenwood.config.js file at root of your project.

Below is a greenwood.config.js file reflecting default values:

export default {
  activeContent: false,
  basePath: "",
  devServer: {
    extensions: [],
    hud: true,
    port: 1984,
    host: "localhost",
  },
  isolation: false,
  layoutsDirectory: "layouts", // e.g. ./src/layouts
  markdown: {
    plugins: [],
    settings: {},
  },
  optimization: "default",
  pagesDirectory: "pages", // e.g. ./src/pages
  plugins: [],
  polyfills: {
    importAttributes: null, // e.g. ['css', 'json']
    importMaps: false,
  },
  port: 8080,
  prerender: false,
  staticRouter: false,
  workspace: new URL("./src/", import.meta.url),
};

Active Content

To enable support for Greenwood's Content as Data capabilities, set the activeContent flag to true.

export default {
  activeContent: true,
};

Base Path

There are cases where an application might be deployed and hosted from a "sub" path that acts as the relative "web root". (GitHub Pages is an example of this)

So with a URL of http://www.example.com/app-a/, the basePath would be set as follows:

export default {
  basePath: "/app-a",
};

This would then configure Greenwood's routing and <script> / <link> tags to reference this segment automatically:

<script type="module" src="/app-a/some-script.a243dccss.js"></script>

For convenience, the value of basePath will also be made available as a global variable in the <head> of your pages:

<script data-gwd="base-path">
  globalThis.__GWD_BASE_PATH__ = "/app-a";
</script>

User content, like <a> and <img> tags will still require manually prefixing the basePath in your application code.

Dev Server

Configuration for Greenwood's development server is available using the devServer object, including the following options:

Below is an example configuration:

export default {
  devServer: {
    extensions: ["txt"],
    port: 3000,
    proxy: {
      "/api": "https://stage.myapp.com",
      "/api/foo": "https://foo.otherdomain.net",
    },
  },
};

Isolation Mode

If running Greenwood as a server in production with the greenwood serve command, it may be desirable to isolate the server rendering of SSR pages and API routes from the global runtime process. This is a common assumption for many Web Component libraries that may aim to more faithfully honor the browser's native specification on the server.

Examples include:

See these discussions for more information

As servers have to support multiple clients (as opposed to a browser tab only serving one client at a time), Greenwood offers an isolation mode that can be used to run SSR pages and API routes in their own context per request.

To configure an entire project for this, simply set the flag in your greenwood.config.js:

export default {
  isolation: true, // default value is false
};

Optionally, you can opt-in on a per SSR page / API route basis by exporting an isolation option:

// src/pages/products.js

export const isolation = true;

Layouts Directory

By default the directory Greenwood will use to look for your layouts is in layouts/. It is relative to your user workspace setting, just like the pages/ directory.

export default {
  layoutsDirectory: "layouts", // Greenwood will look for layouts at src/layouts/
};

Markdown

You can install and provide custom unifiedjs presets and plugins to further customize and process your markdown past what Greenwood does by default.

For plugins, after installing their packages, you can provide their names to Greenwood:

export default {
  markdown: {
    settings: { commonmark: true },
    plugins: ["rehype-slug", "rehype-autolink-headings"],
  },
};

Optimization

Greenwood provides a number of different ways to send hints to Greenwood as to how JavaScript and CSS tags in your HTML should get loaded by the browser. Greenwood supplements, and builds up on top of existing resource "hints" like preload and prefetch.

OptionDescriptionUse Cases
defaultWill add a preload link tag for every script or link tag in the head of your HTML, setting the preload attribute for styles and the modulepreload attribute for scripts. This setting will also minify all your JS and CSS files.General purpose.
inlineUsing this setting, all your script and link tags will get inlined right into your HTML.For sites with smaller payloads, this could work best as with inlining, you do so at the expense of long-term caching.
noneWith this setting, none of your JS or CSS will be minified at all.The best choice if you want to handle everything yourself through custom Resource plugins.
staticOnly for script tags, but this setting will remove script tags from your HTML.If your Web Components only need a single render just to emit some static HTML, or are otherwise not dynamic or needed at runtime, this will help ship unnecessary JavaScript to the client.

Additional improvements and considerations include adding none override support, SSR + hydration, and side effect free layouts and pages.

Here is an example of setting the inline setting:

export default {
  optimization: "inline",
};

Overrides

Additionally, you can apply overrides on a per <link> or <script> tag basis by adding a custom data-gwd-opt attribute to your HTML. The following is supported for JavaScript and CSS.

<!-- Javascript -->
<script type="module" src="/path/to/file1.js" data-gwd-opt="static"></script>
<script type="module" src="/path/to/file2.js" data-gwd-opt="inline"></script>

<!-- CSS -->
<link rel="stylesheet" href="/path/to/file1.css" data-gwd-opt="inline" />

Pages Directory

By default the directory Greenwood will use to look for your local content is pages/. It is relative to your user workspace setting.

export default {
  pagesDirectory: "docs", // Greenwood will look for pages at ./src/docs/
};

Plugins

This takes an array of plugins either created already by the Greenwood team, or one you made yourself.

import { greenwoodCssModulesPlugin } from "@greenwood/plugin-css-modules";

export default {
  plugins: [greenwoodCssModulesPlugin()],
};

Polyfills

Greenwood provides polyfills for a few Web APIs out of the box.

Import Maps

Only applies to development mode.

If you are developing with Greenwood in a browser that doesn't support import maps, with this flag enabled, Greenwood will add the ES Module Shims polyfill to provide support for import maps.

export default {
  polyfills: {
    importMaps: true,
  },
};

Import Attributes

Import Attributes, which are the underlying mechanism for supporting CSS and JSON module scripts, are not widely supported in all browsers yet. Greenwood can enable this in a browser compatible why by specifying which attributes you want handled. In both cases, Greenwood bundles these as ES Modules and will strip the attributes syntax.

export default {
  polyfills: {
    importAttributes: ["css", "json"],
  },
};

In the case of CSS, Greenwood will inline and export your CSS as a Constructable Stylesheet

So given this usage:

import sheet from "./styles.css" with { type: "css" };

The fallback will become this:

const sheet = new CSSStyleSheet();
sheet.replaceSync(" /* ... */ ");
export default sheet;

For JSON, Greenwood will simply export an object.

So this usage:

// this
import data from "./data.css" with { type: "json" };

Will fallback to this:

export default {
  /* ... */
};

Port

Unlike the port option for devServer configuration, this option allows you to configure the port that your production server will run on when running greenwood serve.

export default {
  port: 8181,
};

Prerender

When set to true Greenwood will prerender your site using WCC and generate HTML from any Web Components you include in your pages and layouts as part of the final static HTML build output.

export default {
  prerender: true,
};

You can combine this with "static" components so that you can just do single pass rendering of your Web Components and get their output as static HTML and CSS at build time without having to ship any runtime JavaScript!

Static Router

⚠️ This feature is experimental. Please follow along with our discussion to learn more.

Setting the staticRouter option to true will add a small router runtime in production for static pages to prevent needing full page reloads when navigation between pages that share a layout. For example, the Greenwood website is entirely static, outputting an HTML file per page however, if you navigate from the Docs page to the Getting Started page, you will notice the site does not require a full page load. Instead, the router will just swap out the content of the page much like client-side SPA router would. This technique is similar to how projects like pjax and Turbolinks work, and like what you can see on websites like GitHub.

export default {
  staticRouter: true,
};

Workspace

Path to where all your project files will be located, provided a valid URL. Default is new URL('./src', import.meta.url) relative to the project's root, where the greenwood.config.js file is located.

For example, to change the workspace to be www/:

export default {
  workspace: new URL("./www/", import.meta.url),
};

Please note the trailing / here as for ESM, as paths must end in a / for directories.