Bibliotecas / App Layout

App Layout

Gerador de layout HTML usado em nossos aplicativos SPA.

@gravity-ui/app-layout · npm package CI

Instalar

npm install --save-dev @gravity-ui/app-layout

Uso

Com express:

import express from 'express';
import {createRenderFunction} from '@gravity-ui/app-layout';

const app = express();

const renderLayout = createRenderFunction();

app.get('/', function (req, res) {
  res.send(
    renderLayout({
      // RenderParams
      title: 'Página inicial',
      bodyContent: {
        root: 'Olá mundo!',
      },
    }),
  );
});

app.listen(3000);

onde

interface RenderParams<Data, Plugins> {
  // Qualquer dado compatível com JSON, será definido em window.__DATA__ na página
  data?: Data;
  // favicon
  icon?: Icon;
  // nonce a ser definido nas tags apropriadas
  nonce?: string;

  // opções comuns
  // Título da página
  title: string;
  // idioma da página, será definido na tag html
  lang?: string;
  isMobile?: boolean;

  // atributos html
  htmlAttributes?: string;
  // conteúdo da tag header
  // tags meta
  meta?: Meta[];
  // tags link
  links?: Link[];
  // tags script
  scripts?: Script[];
  // folhas de estilo
  styleSheets?: Stylesheet[];
  // tags script com código inline
  inlineScripts?: string[];
  // tags style com estilos inline
  inlineStyleSheets?: string[];

  // conteúdo da tag body
  bodyContent?: {
    // nome da classe para a tag body
    className?: string;
    // atributos do body
    attributes?: string;
    // conteúdo do body antes da tag div com id root
    beforeRoot?: string;
    // conteúdo innerHtml da tag div com id root
    root?: string;
    // conteúdo do body depois da tag div com id root
    afterRoot?: string;
  };
  // opções de plugins
  pluginsOptions?: Partial<PluginsOptions<Plugins>>;
}

Meta

Descreve a tag meta:

interface Meta {
  name: string;
  content: string;
}

Exemplo:

const meta = [
  {name: 'description', content: 'algum texto'},
  {name: 'robots', content: 'noindex'},
  {name: 'og:title', content: 'Algum título'},
];

Será renderizado como:

<meta name="description" content="algum texto" />
<meta name="robots" content="noindex" />
<meta property="og:title" content="Algum título" />

Icon

Descreve o favicon da página:

interface Icon {
  type?: string;
  sizes?: string;
  href?: string;
}

O valor padrão é:

const icon = {
  type: 'image/png',
  sizes: '16x16',
  href: '/favicon.png',
};

Descreve a tag link:

interface Link {
  as?: string;
  href: string;
  rel?: string;
  type?: string;
  sizes?: string;
  title?: HTMLLinkElement['title'];
  crossOrigin?: '' | 'anonymous' | 'use-credentials';
}

Exemplo:

const link = {
  href: 'myFont.woff2',
  rel: 'preload',
  as: 'font',
  type: 'font/woff2',
  crossOrigin: 'anonymous',
};

será renderizado como:

<link href="myFont.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous" />

Scripts

Descreve o link para o script com preload:

interface Script {
  src: string;
  defer?: boolean;
  async?: boolean;
  crossOrigin?: '' | 'anonymous' | 'use-credentials';
  type?: 'importmap' | 'module' | string;
}

Exemplo:

const script = {
  src: 'url/to/script',
  defer: true,
  async: false,
  crossOrigin: 'anonymous',
};

será renderizado como:

<link href="url/to/script" rel="preload" as="script" crossorigin="anonymous" />

<script src="url/to/script" defer="true" async="false" crossorigin="anonymous" nonce="..."></script>

Folhas de estilo

Descrevem o link para os estilos:

interface Stylesheet {
  href: string;
}

Exemplo:

const styleSheet = {
  href: 'url/to/stylesheet',
};

será renderizado como:

<link href="url/to/stylesheet" rel="stylesheet" />

Plugins

A função de renderização pode ser estendida por plugins. Um plugin pode reescrever o conteúdo de renderização definido pelo usuário. Um plugin é um objeto com as propriedades name e apply:

interface Plugin<Options = any, Name = string> {
  name: Name;
  apply: (params: {
    options: Options | undefined; // passado através da função `renderLayout` no parâmetro `pluginsOptions`.
    commonOptions: CommonOptions;
    renderContent: RenderContent;
    /** @deprecated use `renderContent.helpers` instead */
    utils: RenderHelpers;
  }) => void;
}

interface CommonOptions {
  name: string;
  title: string;
  lang?: string;
  isMobile?: boolean;
}

export interface HeadContent {
  scripts: Script[];
  helpers: RenderHelpers;
  links: Link[];
  meta: Meta[];
  styleSheets: Stylesheet[];
  inlineStyleSheets: string[];
  inlineScripts: string[];
  title: string;
}

export interface BodyContent {
  attributes: Attributes;
  beforeRoot: string[];
  root?: string;
  afterRoot: string[];
}

export interface RenderContent extends HeadContent {
  htmlAttributes: Attributes;
  bodyContent: BodyContent;
}

export interface RenderHelpers {
  renderScript(script: Script): string;
  renderInlineScript(content: string): string;
  renderStyle(style: Stylesheet): string;
  renderInlineStyle(content: string): string;
  renderMeta(meta: Meta): string;
  renderLink(link: Link): string;
  attrs(obj: Attributes): string;
}

Existem alguns plugins neste pacote:

Google analytics

Adiciona o contador do Google Analytics na página.

Uso:

import {createRenderFunction, createGoogleAnalyticsPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createGoogleAnalyticsPlugin()]);
app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        googleAnalytics: {
          useBeaconTransport: true, // habilita o uso de navigator.sendBeacon
          counter: {
            id: 'algum id',
          },
        },
      },
    }),
  );
});

Opções do plugin:

interface GoogleAnalyticsCounter {
  id: string;
}

interface GoogleAnalyticsOptions {
  useBeaconTransport?: boolean;
  counter: GoogleAnalyticsCounter;
}

Yandex Metrika

Adiciona contadores do Yandex Metrika à página.

Uso:

import {createRenderFunction, createYandexMetrikaPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createYandexMetrikaPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        yandexMetrika: {
          counter: {
            id: 123123123,
            defer: true,
            clickmap: true,
            trackLinks: true,
            accurateTrackBounce: true,
          },
        },
      },
    }),
  );
});

Opções do plugin:

export type UserParams = {
  [x: string]: boolean | string | number | null | UserParams;
};

export interface MetrikaCounter {
  id: number;
  defer: boolean;
  clickmap: boolean;
  trackLinks: boolean;
  accurateTrackBounce: boolean | number;
  webvisor?: boolean;
  nonce?: string;
  encryptedExperiments?: string;
  triggerEvent?: boolean;
  trackHash?: boolean;
  ecommerce?: boolean | string;
  type?: number;
  userParams?: UserParams;
}

export type MetrikaOptions = {
  src?: string;
  counter: MetrikaCounter | MetrikaCounter[];
};

Layout

Adiciona scripts e estilos do arquivo de manifesto de assets do webpack.

Uso:

import {createRenderFunction, createLayoutPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createLayoutPlugin({manifest: 'path/to/assets-manifest.json', publicPath: '/build/'})]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        layout: {
          name: 'home',
        },
      },
    }),
  );
});

Opções do plugin:

export interface LayoutOptions {
  name: string;
  prefix?: string;
}

@gravity-ui/uikit

Adiciona atributos ao body.

Uso:

import {createRenderFunction, createUikitPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createUikitPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        uikit: {
          theme: 'dark',
          direction: 'ltr',
        },
      },
    }),
  );
});

Opções do plugin:

interface UikitPluginOptions {
  theme: string;
  direction?: 'ltr' | 'rtl';
}

Remote Versions

Adiciona informações de versões de microfrontends à página.

Este plugin cria um objeto global window.__REMOTE_VERSIONS__ contendo as versões dos microfrontends fornecidas, que pode ser usado por module federation ou arquiteturas de microfrontend semelhantes para determinar quais versões de módulos remotos carregar.

Pode ser usado em combinação com App Builder e a opção moduleFederation.remotesRuntimeVersioning para carregar automaticamente módulos remotos com as versões correspondentes.

Uso:

import {createRenderFunction, createRemoteVersionsPlugin} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction([createRemoteVersionsPlugin()]);

app.get((req, res) => {
  res.send(
    renderLayout({
      title: 'Página inicial',
      pluginsOptions: {
        remoteVersions: {
          header: '1.2.3',
          footer: '2.1.0',
          sidebar: '0.5.1',
        },
      },
    }),
  );
});

Opções do plugin:

type RemoteVersionsPluginOptions = Record<string, string>;

Helpers

Existe um helper para criar todos os plugins:

import {createMiddleware, createDefaultPlugins} from '@gravity-ui/app-layout';

const renderLayout = createRenderFunction(
    createDefaultPlugins({layout: {manifest: 'path/to/assets-manifest.json'}})
);

app.get((req, res) => {
    res.send(renderLayout({
        title: 'Página inicial',
        pluginsOptions: {
            layout: {
                name: 'home'
            },
            googleAnalytics: {
                counter: {...}
            },
            yandexMetrika: {
                counter: {...}
            },
        },
    }));
})

Uso alternativo

Com renderizadores de partes generateRenderContent, renderHeadContent, renderBodyContent via streaming de HTML:

import express from 'express';
import htmlescape from 'htmlescape';
import {
  generateRenderContent,
  renderHeadContent,
  renderBodyContent,
  createDefaultPlugins,
} from '@gravity-ui/app-layout';

const app = express();

app.get('/', async function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/html',
    'Transfer-Encoding': 'chunked',
  });

  const plugins = createDefaultPlugins({layout: {manifest: 'path/to/assets-manifest.json'}});

  const content = generateRenderContent(plugins, {
    title: 'Página inicial',
  });

  const {htmlAttributes, helpers, bodyContent} = content;
        <!DOCTYPE html>
        <html ${helpers.attrs({...htmlAttributes})}>
        <head>
            ${renderHeadContent(content)}
        </head>
        <body ${helpers.attrs(bodyContent.attributes)}>
            ${renderBodyContent(content)}
    `);

  const data = await getUserData();

  res.write(`
            ${content.renderHelpers.renderInlineScript(`
                window.__DATA__ = ${htmlescape(data)};
            `)}
        </body>
        </html>
    `);
  res.end();
});

app.listen(3000);
Sobre a biblioteca
Estrelas
4
Versão
2.3.0
Última atualização
22.08.2025
Repositório
github.com/gravity-ui/app-layout
Licença
MIT License
Contribuidores