Content Component Style Overlap Issue

Hello Builder.io Community,

I am experiencing a styling issue in my Remix + Hydrogen project using Builder.io. When rendering both a page model and a section model (like a footer) on the same route, the Builder.io styles are applied twice. This leads to the footer’s styles overwriting the page’s styles unexpectedly.

•Framework: Remix
Integration: Builder.io with @builder.io/sdk-react/edge

•Setup:
A page model (Content model=page) for the main content.|
A section model (Content model=footer) is rendered dynamically for the footer.|
Both are rendered within the same route, using Builder’s Content component.|
Custom components are registered properly;

*Problem

When both models are rendered on the same page, the styles injected by Builder.io (e.g., .builder-button { all: unset; }) are applied twice. This causes the styles from the footer section to overwrite those of the page model.

root.tsx - Loads the footer model with fetch one entry, pass this to the next component:

PageLayout.tsx - Receive the footer model and render it with component

($slug)._index - loads and render page model

Bug on the page, I’ve encountered two style sheets exactly the same

So it leads the button style to be unseated to the page model

Questions

  1. Is there a recommended way to isolate styles for different Builder.io models on the same route?

  2. Can we conditionally or dynamically control the injection of Builder.io’s stylesheets?

3.Has anyone else encountered this issue, and how did you resolve it?

Hello @hericlis,

It looks like you’re encountering a styling issue where the styles for the footer model are affecting the page model, causing unintended overrides. This is likely because Builder.io injects global styles that are applied across all components by default, and both the page and footer models are sharing the same global styles. Since these styles are not scoped to specific components, they can conflict with each other when rendered on the same route.

Here are some approaches to address this issue:

1. Isolate Styles for Each Model (Scoped Styles)

One way to isolate styles between the models is by applying CSS scoping or using CSS Modules or Styled Components to ensure styles are not globally applied. You can also wrap each Builder.io model in a container element with a unique class and scope the styles to that container.

Option A: CSS Modules (or styled-components)

If you’re using CSS Modules or Styled Components, you can scope the styles per component. For example:

// PageModel.tsx
import styles from './PageModel.module.css';

const PageModel = ({ content }) => {
  return (
    <div className={styles.pageModel}>
      {/* Render content here */}
    </div>
  );
};

export default PageModel;
/* PageModel.module.css */
.pageModel {
  /* page-specific styles */
}

.pageModel .builder-button {
  /* specific styling for buttons on page */
}

Option B: Scoped Classes with plain CSS

If you’re using plain CSS, you can scope your styles by applying a unique class to the top-level container for each model:

// PageLayout.tsx
import { BuilderComponent } from '@builder.io/react';

const PageLayout = ({ footerModel, pageModel }) => (
  <div>
    <div className="page-container">
      <BuilderComponent model="page" content={pageModel} />
    </div>
    <div className="footer-container">
      <BuilderComponent model="footer" content={footerModel} />
    </div>
  </div>
);

export default PageLayout;
/* Scoped styles */
.page-container .builder-button {
  /* styles specific to page */
}

.footer-container .builder-button {
  /* styles specific to footer */
}

2. Dynamically Control the Injection of Builder.io Styles

Builder.io automatically injects a global stylesheet containing all the styles for each model. If the same styles are being injected twice, it could be because the stylesheet is being applied to both the page and footer, causing conflicts.

One way to prevent this is to dynamically inject styles only for the specific model that is being rendered. You could control the loading of these styles by using useEffect or by manually controlling the injection based on the model.

Here’s a possible solution for conditionally loading the styles for Builder.io:

Example: Dynamically Inject Styles for Each Model

import { useEffect } from 'react';
import { BuilderComponent, builder } from '@builder.io/react';

const PageLayout = ({ pageModel, footerModel }) => {
  useEffect(() => {
    // Dynamically inject page-specific styles
    const pageStylesheet = document.createElement('link');
    pageStylesheet.rel = 'stylesheet';
    pageStylesheet.href = builder.getAllContent({ model: 'page' })[0]?.data?.stylesUrl;
    document.head.appendChild(pageStylesheet);

    return () => {
      document.head.removeChild(pageStylesheet); // Clean up when the component is unmounted
    };
  }, []);

  return (
    <div>
      <BuilderComponent model="page" content={pageModel} />
      <BuilderComponent model="footer" content={footerModel} />
    </div>
  );
};

3. Prevent Duplicate Style Injection

Another way to manage this issue is by ensuring that Builder.io stylesheets are not injected multiple times. If Builder.io is injecting the same stylesheet for both the page and footer models, you can manually control when styles are injected. Ensure that the styles for the page model are loaded first and only inject the styles for the footer model once.

You can check for the existing <link> tag in the head to avoid injecting it again:

useEffect(() => {
  const existingStylesheet = document.querySelector("link[href*='builder.io']");
  if (!existingStylesheet) {
    const pageStylesheet = document.createElement('link');
    pageStylesheet.rel = 'stylesheet';
    pageStylesheet.href = builder.getAllContent({ model: 'page' })[0]?.data?.stylesUrl;
    document.head.appendChild(pageStylesheet);
  }

  return () => {
    if (existingStylesheet) {
      document.head.removeChild(existingStylesheet);
    }
  };
}, []);

4. Use Custom CSS to Overwrite Unwanted Styles

If necessary, you can also use !important to override unwanted styles, although this is generally not the most elegant solution. You could, for instance, enforce your page’s styles over the footer’s styles for specific elements that are getting overridden.

.builder-button {
  all: unset !important; /* Use !important only when necessary */
}

5. Using Builder.io’s Advanced Layout and Nesting Capabilities

Builder.io allows for powerful nesting of components, so consider moving the footer out of the page-level model and nesting it within the page model itself (if that fits your design). This way, the styles for the page and footer are confined to one scope, which may help reduce issues with conflicting styles.


Summary

To fix this issue, I recommend using one or more of the following strategies:

  1. Scope styles for each Builder.io model (e.g., using CSS Modules, scoped classes, or Styled Components).
  2. Dynamically inject styles only when needed, based on the rendered model.
  3. Control the injection of Builder.io stylesheets to avoid duplication.
  4. Use custom CSS overrides as a last resort if necessary.

Isolating the styles for each content model and controlling the injection of the stylesheets should solve the problem of conflicting styles between your page and footer models.

Let me know if you need more detailed examples or further assistance!