Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global Component Wrapper #140

Open
1 task done
Mxs2019 opened this issue Jul 29, 2022 · 4 comments
Open
1 task done

Global Component Wrapper #140

Mxs2019 opened this issue Jul 29, 2022 · 4 comments
Labels
enhancement New feature or request

Comments

@Mxs2019
Copy link

Mxs2019 commented Jul 29, 2022

Description

From time to time you want to add a global component wrapper across ALL templates. This could be something UI related or could be something like an analytics provider or a bug reporting provider.

Today the only way to do that is to manually add it to each individual template and wrap each template in shared code. You can abstract this into a <Layout> component but that is still tedious and error prone.

Next.js has app.js and a document.js for exactly this reason. Something similar would be helpful.

Suggested solution

Do something similar to next.js with their app.js and document.js. Remix has an entry.client.js which is kinda similar as well. https://remix.run/docs/en/v1/api/conventions#entryclienttsx.

I'd propose an app.js file that lives in the src repo. It allows you add global components that wrap around every template. Under the hood it would do the exact same thing as individually wrapping each page. So something like where <Wrapper> is a custom component

function MyApp({ Component, pageProps }) {
  return <Wrapper><Component {...pageProps} /></Wrapper>
}

export default MyApp

Alternative

No response

Additional context

No response

Validations

  • Check that there isn't already an issue that requests the same feature to avoid creating a duplicate.
@mkilpatrick mkilpatrick added the enhancement New feature or request label Jul 29, 2022
@tmeyer2115
Copy link
Contributor

Consulting has added an AnalyticsProvider wrapper. If we were to add the concept of a layout, it would no longer live in this repo. Maybe @yext/react-components.

@Mxs2019
Copy link
Author

Mxs2019 commented Dec 6, 2022

I think we would need first class support for this in pages. Otherwise you will have to import the <Layout> in every single template which is what I'm trying to avoid. I think this feature is more a root.tsx file or something where you can add stuff that wraps all your pages.

@Mxs2019 Mxs2019 reopened this Dec 6, 2022
@Ceseura
Copy link
Contributor

Ceseura commented Jul 12, 2023

Are there any updates on this? I think a template like this would be quite helpful for implementing i18n support, as well as for adding a Custom Field usage highlighter, and other things that require interacting with the entire page from the top-most level component. Our current implementations involve pasting the same component into each template file index.tsx directory.tsx etc which just wraps the normal exported template. Adding this feature could remove a lot of replicated code.

@mkilpatrick
Copy link
Collaborator

mkilpatrick commented Jul 12, 2023

We actually have support for a single global _client.tsx and _server.tsx (in src/templates) that gives you full React rendering control. There's no documentation on it yet, but here's an example. Just note that it's global for every template.

_client.tsx

// Environment: browser

import * as ReactDOM from "react-dom";
import * as React from "react";
import { PageContext } from "@yext/pages";

export { render };

const render = async (pageContext: PageContext<any>) => {
  const { Page, pageProps } = pageContext;
  ReactDOM.hydrate(
    <Page {...pageProps} />,
    document.getElementById("reactele"),
  );
};
_server.tsx

// Environment: server

import * as ReactDOMServer from "react-dom/server";
import * as React from "react";
import { PageContext } from "@yext/pages";

export { render };

const render = async (pageContext: PageContext<any>) => {
  const { Page, pageProps } = pageContext;
  const viewHtml = ReactDOMServer.renderToString(<Page {...pageProps} />);

  return `<!DOCTYPE html>
    <html lang="<!--app-lang-->">
      <head></head>
      <body>
        <div id="reactele">${viewHtml}</div>
      </body>
    </html>`;
};

Another option is to create your own layout wrapper than you call in each template, but sounds like you're looking to avoid this.

/src/layouts/foo.tsx

export const Foo = (children) => {
  return <Somei18nThing>{children}</Somei18nThing>
}
src/templates/location.tsx

const Location: Template<TemplateRenderProps> = ({
  relativePrefixToRoot,
  document,
}) => {
  ...
  return <Foo>.....</Foo>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants