Skip to main content

Client-side assets

A podlet will likely depend on some CSS and maybe client-side JavaScript to work properly. When a layout composes podlets it has to include each podlet's assets in the final document. This poses some novel challenges.

  • Where to host the client-side assets?
  • How to handle duplication of shared libraries such as React?
  • How to isolate styling or behavior between podlets and layout?

Hosting assets

There are two main options for hosting assets:

  • The podlet can serve its own assets
  • Use a separate asset server or CDN

Podlet serves assets

Note: this will only work if your podlets are publicly available.

This approach involves each podlet serving its assets so that the layout can then include these files in its HTML template.

Step 1.

In your podlet, use the podlet asset helper functions to define inline client code.

podlet.js({ value: `http://my-podlet.com/assets/scripts.js` });
podlet.css({ value: `http://my-podlet.com/assets/styles.js` });

Each of these functions can be called multiple times to add additional assets. For each call, you may also set a type.

podlet.js({ value: `http://my-podlet.com/assets/scripts1.js`, type: "esm" });
podlet.js({
value: `http://my-podlet.com/assets/scripts2.js`,
type: "default",
});

Step 2.

Serve the assets from express. Assuming the podlets client side assets have been placed in a directory called assets:

app.use("/assets", express.static("assets"));

See the Express documentation for more information on static.

Step 3.

Set incoming.podlets and use podiumSend in your layout's request handler. This way the document template can include the CSS and JS assets served by the podlet.

app.get(layout.pathname(), (req, res) => {
const incoming = res.locals.podium;
const response = await myPodlet.fetch(incoming);

incoming.podlets = [response];
res.podiumSend(`<div>Hello, Layout</div>`);
});

Use a CDN

This approach involves each podlet uploading its assets to a predefined CDN location so that the layout can then include the CDN URLs in its HTML response.

Step 1.

In your podlet, upload your assets to a CDN. You might do this whenever your podlet server is built or starts up to ensure the latest version is available on the CDN.

Step 2.

Next, tell the podlet the location of your assets so that it can populate the manifest file.

podlet.js({ value: "http://some-cdn.com/client.js" });
podlet.css({ value: "http://some-cdn.com/style.css" });

Step 3.

Set incoming.podlets and use podiumSend in your layout's request handler. This way the document template can include the CSS and JS assets served by the podlet.

app.get(layout.pathname(), (req, res) => {
const incoming = res.locals.podium;
const response = await myPodlet.fetch(incoming);

incoming.podlets = [response];
res.podiumSend(`<div>Hello, Layout</div>`);
});

Deduplicating shared dependencies

It's likely one or more of your podlets share a common dependency, such as React. Unless you take action each podlet will bundle its own complete copy of React, wasting bandwith and execution time.

It's up to you to configure your build tools and infrastructure so you can avoid this duplication in your bundles and serve shared dependencies in a performant way.

You may want to look into Eik and its build tool plugins, which were built by the same team that maintains Podium to solve this performance problem.

Isolation

A podlet should ideally not affect or be affected by the layout or other podlets. This can be tricky, particularly for CSS because of its global nature and the cascade.

Unique selectors

You can work around the isolation problem by adopting a namespacing convention for all CSS selectors. CSS modules and other similar tools that generate unique selectors can also help mitigate the isolation problem.

Unique selectors can mitigate some of the isolation problems, but a podlet can still be affected by the layout's CSS.

Declarative shadow DOM

Using the shadow DOM you can isolate a podlet from its surroundings. By wrapping a podlet in a declarative shadow DOM you can still get the benefits of server-side rendering.

To wrap a podlet in declarative shadow DOM, set the useShadowDOM option to true.

Any CSS added must set strategy: "shadow-dom" so it can be linked to and apply inside of the shadow DOM.

const podlet = new Podlet({
name: "my-podlet", // when useShadowDOM is set to true, name must be valid custom element name
useShadowDOM: true,
});

podlet.css({
value: "https://cdn.site.com/my-podlet.css",
strategy: "shadow-dom",
});

Islands architecture

Podium works well with islands architecture where interactivity on the client is handled by small, isolated applications.

Especially when building your layout:

  • Consider how JavaScript libraries you use handle external content (external in the sense that it is not generated by your library).
  • Be mindful of how much of the document your JavaScript library hydrates.