Working with assets

Thanks to its solid Vite foundations, you have plenty of ways to work with any kind of static or optimized assets, from fonts to WASM.

Scripts and styles via HTML tags

There are multiple cases for using HTML tags for importing client-side JS or CSS:

1. Globally, at the document level

When you are associating multiple routes with the same enclosing document, you might want to inject shared assets for them.

<link rel="stylesheet" href="/src/document.scss" />
<script type="module" src="/src/document.client.ts"></script>

You can always add more link or script, but it’s probably cleaner to add CSS @import or JS import in single entry points, for you own sources or those from vendors.

Important

While it is strongly recommended to use .js even for TS, and relative paths, not absolute, these rules won’t apply in HTML when using Vite / Rollup based systems for bundle entrypoints. You have to provide the full real path relative to the project root, like /src/components/my-element.ts.
Same for non-vanilla CSS.

Alternatively, you can use this notation, with URL and import.meta.url:

new URL('./my-asset.css', import.meta.url).pathname;

E.g.

📄 /src/document.ts

import { html } from '@gracile/gracile/server-html';

export const document = html`
  <head>
    ...
    <link
      rel="stylesheet"
      href=${new URL('./document.css', import.meta.url).pathname}
    />
    <script
      type="module"
      src=${new URL('./document.client.ts', import.meta.url).pathname}
    ></script>
    ...
  </head>
`;

It’s arguably more verbose but it is also a more canonical way to achieve this endeavour.
Dealing with relative paths can be preferable, too.

For both dev. and build, Vite will resolve those paths for you properly.


This works, too, for explicit entry point grouping:

<script type="module">
  import '/src/document.client.ts';
  import '/src/my-lib.ts';
</script>

Note that Vite will deduplicate/bundle per-route assets properly for build. E.g.

<script type="module" src="/src/alpha.ts"></script>
<script type="module" src="/src/beta.ts"></script>
<script type="module" src="..."></script>

 It becomes one after build 
<script type="module" src="/assets/page-bundle-123h4sh.js"></script>

2. Per-route assets injection

📄 /src/document.ts

import { html } from '@gracile/gracile/server-html';

export const document = () => html`
  <!doctype html>
  <html lang="en">
    <head>
      ...

       Route sibling page assets will be appended here, right before the closing HEAD 
    </head>

    ...
  </html>
`;

Given:

  • project/
    • src/
      • routes/
        • my-page.ts
        • my-page.css (or .{scss,less,…})
        • my-page.client.ts

Result in:

<link rel="stylesheet" href="/src/routes/my-page.css" />
<script type="module" src="/src/routes/my-page.client.ts"></script>

This mechanism isn’t here just for convenience, but to make to streamline the way the server handler will be generated (but it’s also useful for static mode).
Putting all route assets at the document level allows for deterministic optimizations by the bundler at build time, which wouldn’t be possible (at least easily) at run time.

3. Anywhere in a server template

You can also put script (module or inline) and link anywhere in your HTML partials or server-rendered Lit Elements, but it’s not a recommended way.

First of all, it makes static analysis harder, for things like modules preloader. Also, it’s generally recommended to put assets in <head> for performance reasons, not in semi-random places in the body.
This rule is not set in stone, though. You might have legitimate reasons for doing otherwise.

For static build, Vite will handle this just fine and apply optimizations on the whole page at once, it will even put modules (async) in the <head> for you, with preloaders.
However for server output, since your page output is not deterministic, those will not be hoisted in the head.
You’ll have to make sure to use the “URL + import.meta.url” syntax, not /src/foo/bar.ts; That way, the correct path will be dynamically injected in place when building the Gracile server handler for production, hence, matching the dev. behavior.

The public/ folder for static assets

As you know, Gracile is built on top of Vite, so naturally, it will inherit Vite’s capabilities like serving assets as-is, from the public directory.

Putting an asset in it, will make it available from the base URL, e.g., /public/favicon.svg/favicon.svg.

More details on the Vite docs.

Exotic file formats

For anything outside the realm of HTML/CSS/JS, you have a large collection of Vite plugins to cater to your needs.

Vite provides ?raw or ?inline flags, web workers loading mechanisms etc. Gracile support this, naturally, but also provides a handful of plugins specifically tailored to html template literals.


Checkout the Add-ons.