Hongbo Miao
Hongbo Miao

Reputation: 49974

How to limit style to component level in React?

My app tries to show emails. Sometimes the style in the email will affect the app itself.

Right now I am using the package juice to inline style in the email html. However, sometimes it cannot inline correctly. So I try to find other solutions.

I know Angular automatically add some random string in each class to make sure style in one component won't affect other component, is there a same way to do it in React? Or is there other way to limit style to the component level without using iframe? Thanks

The demo shows the p { color: red; } from the email also affects the app itself. In this case, it affects Content in app.

Live demo

class Mail extends Component {
  render() {
    // the style inside is from mail, in real case, it can have tens or even hundreds of different styles
    const mailFromServer = `
      <html>
        <head>
          <style>
            p { color: red; }
          </style>
        </head>

        <body>
          <p>Content in mail</p>
        </body>
      </html>
    `;

    return (
      <div>
        <div dangerouslySetInnerHTML={{__html: mailFromServer}} />
      </div>
    );
  }
}

class App extends Component {
  render() {
    return (
      <div>
        <Mail />

        <p>Content in app</p>
      </div>
    );
  }
}

Upvotes: 9

Views: 4929

Answers (5)

CSSBurner
CSSBurner

Reputation: 1991

Even though browser support is not great (48%) at the moment for @scope, it can be used to selectively target ancestor subtrees. For example, in this JSFiddle (please run in Chrome November 2023 due to browser support limitations), you can see that the .target sub-tree is selected for:

p {
    color: red;
    font-size: 24px
}

@scope (.target) {
    p {
        color: green
    }
}

In your demo case above, try using @scope like this:

class Mail extends Component {
    render() {
        const mailFromServer = `
            <html>
                <head>
                    <style>
                        @scope (.target) { // added @scope
                            p { color: red; }
                        } 
                    </style>
                </head>
                <body class=target> <!-- added scoping class -->
                    <p>Content in mail</p>
                </body>
            </html>`;
        return (
           <div>
               <div dangerouslySetInnerHTML={{__html: mailFromServer}} />
           </div>
        );
    }
}

UPDATE May 2024:

With good browser support (75%) in May of 2024, the @scope rule is well suited for your use case. For example, here is an example from one of my React apps (attached)enter image description here, where the @scope rule has been applied to #journal-section only (giving this input field a bold, red text style.

Input fields of other components retain their specific text style and do NOT take on @scope style.

enter image description here

Upvotes: 0

Napoli
Napoli

Reputation: 1403

Yes, you can use CSS Modules, which is one or more css files (written in js) in which all class names are auto-scoped locally to each component by default.

There is a good article about this here https://medium.com/@pioul/modular-css-with-react-61638ae9ea3e

You can also try to reset those known html entities which are commonly over-ridden by your Mail html. You may have to play around a bit, but, given your example, you could do something like the following:

https://stackblitz.com/edit/react-neymbb

Basically, in my sandbox I reset your paragraph color and padding to their defaults, as well as, used it in conjunction with another inline style. Depending on the entire construct of css inheritance in your app, your results may vary, but you should be able to get something workable.

More information on the css values I recommend found here: https://developer.mozilla.org/en-US/docs/Web/CSS/initial

Upvotes: 0

Kunal arora
Kunal arora

Reputation: 175

There are few ways to do this.One of the way would be by passing style to the elements and defining styles as objects. For example

const styles = {
        content: {
            color: '#000',
            backgroundColor: '#fafafa',
        },
    };

class Mail extends React.Component {
    render() {
        return() {
            <p style={{styles.content}}> Content </p>
        }
    }
}

If you really want something scalable then you can use styled-components which for me personally work really nicely and fulfills all your styling needs.

Upvotes: 3

Eric
Eric

Reputation: 2705

Styles in <style>..</style> or from CSS sheet is global. They are applied across your app.

If you have control over how the email is formatted, I would recommend setting different class names for different email types.

If you have a limited set of email types, you can specify the styles for each of them in a css sheet, and import it on your html file or Webpack.

styles.css

.important-email { color: red; }
.not-important-email { color: blue; }
// ...
// styles for different email types    

Mail.jsx

class Mail extends Component {
  render() {
    const mail = `
      <html>
        <head>
        </head>

        <body>
          <h1 class="important-email">Heading in mail</h1>
        </body>
      </html>
    `;

    return (
      <div>
        <div dangerouslySetInnerHTML={{__html: mail}} />
      </div>
    );
  }
}
export default Mail;

Upvotes: 0

treyhakanson
treyhakanson

Reputation: 4921

You'll need to use a class to limit the style to a specific component's features:

import React, { Component } from 'react';
import { render } from 'react-dom';

class Mail extends Component {
  render() {
    const mail = `
      <html>
        <head>
          <style>
            .mail-header { color: red; }
          </style>
        </head>

        <body>
          <h1 class="mail-header">Heading in mail</h1>
        </body>
      </html>
    `;

    return (
      <div>
        <div dangerouslySetInnerHTML={{__html: mail}} />
      </div>
    );
  }
}
export default Mail;

Upvotes: 0

Related Questions