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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@psu-flex/core-ui-federated-wc

v1.4.7

Published

Registers PSU chrome as **vanilla custom elements** (shadow DOM) for external apps: Angular, static HTML, Drupal, etc.

Downloads

252

Readme

@psu-flex/core-ui-federated-wc

Registers PSU chrome as vanilla custom elements (shadow DOM) for external apps: Angular, static HTML, Drupal, etc.

Built on @psu-flex/chrome-elements — no React, no r2wc, no @psu-flex/wp-wc-resolver.

Components

| Custom element | Content | | ------------------------ | ------------------------- | | <psu-brand-bar> | Brand bar + search flyout | | <psu-mega-menu-header> | Mega menu header | | <psu-site-footer> | Site footer | | <psu-brand-footer> | Brand footer |

Tag rename: Legacy tags were psf-*. Current tags are psu-*.

Internal Next.js apps

PSU Flex Next.js apps should not use this package. Use the server/CTF path instead:

  • @psu-flex/core-ui-federatedBrandBarNewCtf, BrandFooterNewCtf
  • @psu-flex/core-ui-layout-navigationMegaMenuHeaderCtf, FooterNewCtf

Those render HTML in the document (not shadow DOM) and load @psu-flex/chrome-elements/*.css automatically.


Quick start

1. Register elements + fonts (once per app)

Angular (main.ts):

import { defineAllPsuElements } from '@psu-flex/core-ui-federated-wc';

defineAllPsuElements();

Next.js / React (client component):

'use client';

import { useEffect } from 'react';
import { defineAllPsuElements } from '@psu-flex/core-ui-federated-wc';

export function FederatedClient() {
  useEffect(() => {
    defineAllPsuElements();
  }, []);
  return null;
}

Importing the package alone does not register tags — you must call defineAllPsuElements().

2. Load document fonts

Theme tokens and component layout CSS live inside each element’s shadow root. Only fonts must be loaded on the document:

Angular angular.json:

"styles": ["node_modules/@psu-flex/wc-styles/dist/psf-styles.css"]

HTML:

<link rel="stylesheet" href="node_modules/@psu-flex/wc-styles/dist/psf-styles.css" />

defineAllPsuElements() also calls injectPsuFonts() (font link injection). Loading psf-styles.css is still recommended for full @font-face coverage.

3. Add tags to your template

<psu-brand-bar></psu-brand-bar>
<psu-mega-menu-header></psu-mega-menu-header>
<!-- page content -->
<psu-site-footer></psu-site-footer>
<psu-brand-footer></psu-brand-footer>

4. Hydrate after fetch

Raw Contentful/API payloads are mapped inside each element. Pass the same JSON shape.

Option A — props (Angular, recommended):

brandBarEl.props = {
  ...data.brandBar,
  isExternal: true,
  extraSearchOptions: searchoptions, // optional, legacy shape
};
headerEl.props = data.header;
footerEl.props = data.footer;
brandFooterEl.props = data.brandFooter;

Option B — data attribute (serialized JSON):

import {
  hydrateChromeElementData,
  hydrateAllPsuChromeElements,
} from '@psu-flex/core-ui-federated-wc';

hydrateAllPsuChromeElements({
  brandBar: { ...data.brandBar, isExternal: true },
  header: data.header,
  footer: data.footer,
  brandFooter: data.brandFooter,
});

// Or per element:
hydrateChromeElementData(document.querySelector('psu-brand-bar'), payload);

Brand bar search (external apps)

External embeds should send users to Penn State search unless you override it.

| Prop | External default | Purpose | | -------------------- | ---------------- | --------------------------------------------------------------- | | isExternal | true (on CE) | Form action → https://www.psu.edu/search | | extraSearchOptions | — | Extra radios; optional slug, queryParamName, urlToInclude | | searchAction | — | Override target URL (usually omit for external) |

Do not set searchAction: '/search' on external sites unless that route exists on your host.

// Correct for external Angular / static hosts
brandBarEl.props = {
  ...data.brandBar,
  isExternal: true,
  extraSearchOptions: mySearchOptions,
};

Fetching data

Cache federated data on the server when possible, then hydrate on the client after defineAllPsuElements().

APIs (PSU Flex endpoints)

| API | Data | | ---------------------------------------------------------------------------------------- | ------------------------------------------- | | fetchAllFederatedData | All chrome (recommended) | | fetchBrandBar | Brand bar only | | fetchHeader | Header only | | fetchFooter | Footer only | | Brand footer | Via fetchAllFederatedDatabrandFooter |

Example — fetch all (Next.js server → client hydrate)

// fetchFederatedData.ts
export async function fetchAllFederatedData() {
  const response = await fetch('https://psu-flex-endpoints.vercel.app/api/fetchAllFederatedData', {
    next: { revalidate: 3600 },
  });
  if (!response.ok) throw new Error('Failed to fetch federated data');
  return response.json();
}
// layout.tsx (server)
import { FederatedClient } from './FederatedClient';
import { fetchAllFederatedData } from './fetchFederatedData';

export default async function RootLayout({ children }) {
  const data = await fetchAllFederatedData();

  return (
    <html lang="en">
      <head>
        <link rel="stylesheet" href="/node_modules/@psu-flex/wc-styles/dist/psf-styles.css" />
      </head>
      <body>
        <FederatedClient data={data} />
        <psu-brand-bar id="psu-brand-bar" />
        <psu-mega-menu-header />
        <main>{children}</main>
        <psu-site-footer />
        <psu-brand-footer />
      </body>
    </html>
  );
}
// FederatedClient.tsx
'use client';

import { useEffect } from 'react';
import { defineAllPsuElements, hydrateAllPsuChromeElements } from '@psu-flex/core-ui-federated-wc';

export function FederatedClient({
  data,
}: {
  data: Awaited<ReturnType<typeof fetchAllFederatedData>>;
}) {
  useEffect(() => {
    defineAllPsuElements();
    if (data) {
      hydrateAllPsuChromeElements({
        brandBar: { ...data.brandBar, isExternal: true },
        header: data.header,
        footer: data.footer,
        brandFooter: data.brandFooter,
      });
    }
  }, [data]);

  return null;
}

Example — Angular

<psu-brand-bar #brandBar></psu-brand-bar>
<psu-mega-menu-header #header></psu-mega-menu-header>
<psu-site-footer #footer></psu-site-footer>
<psu-brand-footer #brandFooter></psu-brand-footer>
// app.component.ts — after fetch
this.brandBar.nativeElement.props = { ...data.brandBar, isExternal: true };
this.header.nativeElement.props = data.header;
this.footer.nativeElement.props = data.footer;
this.brandFooter.nativeElement.props = data.brandFooter;

Use CUSTOM_ELEMENTS_SCHEMA in your Angular component.


API reference

defineAllPsuElements()

Registers all four custom elements and injects document fonts. Call once before hydrating.

hydrateChromeElementData(el, payload)

Sets element.data / data attribute with JSON (same contract as legacy r2wc data: 'string').

hydrateAllPsuChromeElements({ brandBar, header, footer, brandFooter })

Hydrates the four default tags in one call.

Individual registrars

import {
  definePsuBrandBar,
  definePsuMegaMenuHeader,
  definePsuSiteFooter,
  definePsuBrandFooter,
  injectPsuFonts,
} from '@psu-flex/core-ui-federated-wc';

Migration from psf-* / r2wc

| Before | After | | ------------------------------------- | ---------------------------------------------------- | | psf-brand-bar | psu-brand-bar | | psf-header | psu-mega-menu-header | | psf-footer | psu-site-footer | | psf-brand-footer | psu-brand-footer | | @psu-flex/wp-wc-resolver in webpack | Not needed | | React BrandBarWcClient + r2wc | defineAllPsuElements() + props / data | | searchoptions prop on React client | extraSearchOptions on props + isExternal: true |


Related packages