wix-incubator / mjml-react

React component library to generate the HTML emails on the fly

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Strategy for associating styles to components rather than at the parent level?

kprokopczyk opened this issue · comments

I have a base template component that i pass my components to and render them inside MjmlBody:

const BaseEmailTemplate = ({ children }) => {
    const styles = readFileSync(resolve(__dirname, '../../assets/styles.css')).toString();
    const mediaQueryStyles = readFileSync(resolve(__dirname, '../../assets/mediaQueries.css')).toString();

    return (
        <Mjml>
            <MjmlHead>
                <MjmlStyle inline>{styles}</MjmlStyle>
                <MjmlStyle>{mediaQueryStyles}</MjmlStyle>
            </MjmlHead>
            <MjmlBody>
                {children}
            </MjmlBody>
        </Mjml>
    );
};

So I'm reading in css files and rendering the styles in MjmlHead, much like your example. For media queries, each of my templates includes ALL the styles, even styles targeted to components that a given template does not include. Since some email clients clip emails over a certain size, I want to only include styles that are relevant to the components included in a given template. Additionally, if I delete a component I want all its style definitions to go with it.

I'm wondering if you have any strategies for moving css to the component level so that only the styles associated with the children are rendered in head. Like ideally i'd like my components organized like this, with css tied to each component:

components/
    BaseEmailTemplate/
        BaseEmailTemplate.jsx
        index.js
        styles.css 
    Button/
        Button.jsx
        index.js
        styles.css

Thanks for any tips.

So I'm reading in css files and rendering the styles in MjmlHead, much like your example.

What example are you referring to? We have been talking about how to solve this problem as well and are planning on taking the approach that styled-components uses for render to string... just haven't had a chance to implement yet

A possible solution to your issue would be to use a post processor on the html. Something like https://emailcomb.com/

The example I'm referring to is just this, where a file is read in: https://github.com/wix-incubator/mjml-react-example/blob/master/src/email.js#L18

A post processor feels like a good stopgap but it still doesn't allow me to couple styles with components, so I'll probably keep looking around for a good solution. Thanks!

yeah agreed. Same reason we aren't doing it

@kprokopczyk we've started using https://github.com/styled-components/styled-components with mjml-react.

This is our "react to mjml" script:

import React from "react";
import ReactDOMServer from "react-dom/server";
import { ServerStyleSheet } from "styled-components";

function getStyledComponentsStaticCss(sheet: any): string {
  const tag = sheet.getTag();

  return [...Array(tag.length).keys()].map((group) => tag.getGroup(group)).join("");
}

export function getMjmlFromReact(element: React.ReactNode) {
  const sheet = new ServerStyleSheet();
  const elementWithCollectedStyles = sheet.collectStyles(element);
  const mjmlWithoutStyledComponentsCss = ReactDOMServer.renderToStaticMarkup(elementWithCollectedStyles);
  const css = getStyledComponentsStaticCss(sheet.instance);
  return mjmlWithoutStyledComponentsCss.replace(/\/\* inject css here \*\//g, css);
}

How to use styled components:

const StyledMjmlText = styled(({className, ...props}) => <MjmlText {...props} cssClass={className} />)`
  height: 250px;
  border: 1px solid red !important;
`;

export const TestingStyles: Story = () => (
  <Mjml>
    <MjmlHead>
      <MjmlStyle>
        {css`
          /* inject css here */
        `}
      </MjmlStyle>
    </MjmlHead>
    <MjmlBody>
      <MjmlSection>
        <MjmlColumn>
          <StyledMjmlText>Hello 250px high typography</StyledMjmlText>
        </MjmlColumn>
      </MjmlSection>
    </MjmlBody>
  </Mjml>
);

This works with deeply nested components. Unfortunately it requires className => css-class which is a little cumbersome.

Based on this repo having basically no support. I'm thinking about just implementing my own version with a bunch more functionality like className => css-class

@IanEdington Did you end up developing / open-sourcing anything to help with styled-components adoption?

@gajus not yet but I could... let me get back to you

I would appreciate that. I spent couple of hours playing with styled-components and Mjml and couldn't make it work.

@gajus I got the go ahead to open source a couple of our mjml tools. I should be able to get them up early next week

@gajus I got the go ahead to open source a couple of our mjml tools. I should be able to get them up early next week

Please ping in this thread if / when you share those tools. 🙏

Sorry for the delay on this. I'm trying to prioritize this work.

Sorry for the delay on this. I'm trying to prioritize this work.

Any chance you could share whatever MVP you've got, even it isn't polished?

@IanEdington please let us know when you share this! <3 Thank you!

Hey all, sorry for leaving you hanging. We have support for this in https://github.com/Faire/mjml-react version 3+

This issue has a short overview of how we use style-components Faire/mjml-react#70

TLDR: We've added className -> cssClass so any react tool that uses className can be used with mjml-react

Amazing! Thanks @IanEdington!