npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@micro-frame/marko

v1.10.1

Published

A Marko tag for building SSR friendly micro frontends.

Downloads

103

Readme

Installation

npm install @micro-frame/marko

How it works

This package exposes a <micro-frame> Marko component that in many ways is similar to a traditional <iframe>. However, unlike an iframe, the content from the src is loaded, with streaming support, directly into the existing document.

On the server

When this component is rendered server side, it will make a request to load the embedded html resource. The response is then streamed along side the content for the host page.

Internally make-fetch-happen is used to perform the requests from the server. These means you can also leverage HTTP Cache-Control.

In the browser

When rendered client side a normal fetch request is made to load the embedded html resource. The content of the response will be rendered within the page as if it was a server side render. This includes full streaming support.

Any time the src attribute is changed, a new request will be made to load updated html content.

Why

This module allows for embedded micro frontends with the following benefits:

  1. Can take full advantage of streaming, if loaded server side or in the browser.
  2. Both the host, and embedded applications simply respond with HTML.
  3. Framework agnostic, the child can respond with HTML generated by any tool/framework.

Specifically in comparison to iframes it offers the following advantages:

  • Usability
    • Control over loading & error state rendering.
    • Does not break navigation / back button.
    • Does not appear differently to screen readers.
    • Does not cause issues using native browser API’s that are sometimes restricted in iframes.
    • Content can rendered with the rest of the page
      • No resizing issues.
      • Flows with page content / layout.
      • Can escape it’s container, eg for modals
  • Performance
    • Shares single connection with host (no round trip once iframe makes it to the browser).
    • Does not impact SEO (sometimes iframes are not indexed by search engines).
    • iframes receive lower priority than other assets on the page, this does not.
    • Avoids additional window / browser context (less memory used).
    • Avoids boilerplate html, just send fragments (no <html>, <head>, etc).
    • Caches in both the client and host server.

Why not

This module works best when you have applications that are independently developed, potentially with different technology stacks, that you want to glue together.

  • Applications broken up this way in general are harder to optimize, deploy, etc.
  • Embedded apps should be served from the same origin/TLD to prevent CORS issues. You should not embed untrusted applications, you should consider the embedded application a part of the host page.
  • There will always be overhead in this approach, or really any naive micro-frontend setup. This module does not dictate how assets are loaded or shared across applications. If necessary that must be orchestrated between the applications separately. Solutions like Module federation, native ES modules & globally available modules should work fine with micro-frame.

Example

<micro-frame src="my-nested-app">
  <@loading>
    We're still loading...
  </@loading>
  <@catch|err|>
    Uh-oh! ${err.message}
  </@catch>
</micro-frame>

API

src

A (required) path to the embedded html application. This is resolved from the origin of the of the host application.

<micro-frame src="my-nested-app"/>

With the above, assuming the host application is rendered at https://ebay.com/n/all-categories, the embedded application will resolve to https://ebay.com/my-nested-app.

headers

Optionally provide additional http headers to send. Only the object form shown below is supported.

<micro-frame src="..." headers={
  "X-My-Header": "Hello",
  "X-Another-Header": "World"
}/>

Note that be default on the server side headers are copied from the current incoming request, the headers option will be merged with existing headers.

cache

Mirrors the Request.cache options (works on both server and client renders).

<!--
This example will always show cached content if available
and fallback to the network otherwise
-->
<micro-frame src="..." cache="force-cache"/>

fetch

Optionally provide function to override default fetch logic.

<micro-frame src="..." name="..." fetch(url, options, fetch) {
  // The 3rd parameter allows us to continue to use micro-frames fetch implementation (which is different server/browser).
  // We can use this override to do things like a POST request, eg:
  return fetch(url, {
    ...options,
    method: "POST",
    headers: {
      ...headers,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ "some": "json" })
  });
}  />

timeout

A timeout in ms (defaults to 30s) that will prematurely abort the request. This will trigger the <@catch> if provided. If set to 0 the request will not time out.

<!--
This example will disable the default 30s timeout.
-->
<micro-frame src="..." timeout=0/>

<@catch|err|>

An attribute tag rendered when there is a network error or timeout. If there is no @catch handler the error will be emitted to the stream, similar to the <await> tag.

<micro-frame src="...">
  <@catch|err|>
    <!-- Displays if request to service fails or times out -->
    error: ${err.message}
  </@catch>
</micro-frame>

<@loading>

An attribute tag rendered when while the request is still being streamed. It is removed after the request has either errored, or successfully loaded.

<micro-frame src="...">
  <@loading>
    We are loading the nested app...
    <my-spinner/>
  </@loading>
</micro-frame>

class

Optional class attribute which works the same way as Marko class attribute.

<micro-frame src="..." class="a c"/>
<micro-frame src="..." class={ a:true, b:false, c:true }/>
<micro-frame src="..." class=["a", null, { c:true }]/>

style

Optional style attribute which works the same way as Marko style attribute.

<micro-frame src="..." style="display:block;margin-right:16px"/>
<micro-frame src="..." style={ display: "block", color: false, marginRight: 16 }/>
<micro-frame src="..." style=["display:block", null, { marginRight: 16 }]/>

client-reorder

Similar to the tag client-reorder attribute this tells the micro-frame to avoid blocking content later in the document.

Note when this is used the micro-frame will be buffered instead of streamed and inserted once it's ready.

<!--
This example will disable the default 30s timeout.
-->
<micro-frame src="..." timeout=0/>

Communicating between host and child

Communicating with the embedded application happens primarily in one of two ways, either you want to do a full reload of and get new HTML, or you want to orchestrate a client side rendered update.

Full reload

To perform a full reload of the embedded application it works best to pass a query string in the src attribute. Whenever src updates, a full reload will happen automatically.

class {
  onCreate() {
    this.state = { page: 0 };
  }

  nextPage() {
    this.state.page++;
  }
}

<micro-frame src=`my-nested-app?page=${state.page}`/>

<button onClick("nextPage")>Next Page</button>

With the above, any time state.page changes the my-nested-app content will be re-loaded.

Client side update

Client side communication between the host and child application can be done through a number of mechanisms. You can use a global store, store data on the dom (perhaps even use web components) or other creative options.

You can do this relatively simply by having a contract between the host and child application. Below is an example using a global exposed by the nested application.

class {
  onCreate() {
    this.state = { page: 0 };
  }

  openModal() {
    if (window.nestedApp) {
      window.nestedApp.openModal();
    }
  }
}

<micro-frame src="my-nested-app"/>

<button onClick("openModal")>Open nested app modal</button>

Code of Conduct

This project adheres to the eBay Code of Conduct. By participating in this project you agree to abide by its terms.