MDX renderer - Gatsby Site - <li>, <pre> and <code> tags not rendering

1.2k views Asked by At

I'm trying to figure out why I have to specify all HTML tags in the gatsby-browser.js in order for the MDXrenderer to understand how to style the HTML tags.

The content comes from GraphCMS via GraphQL queries.

I've been looking into other gatsby projects, where the specifications are not set, but they render HTML tags correctly.

I was able to target most HTML tags, but I cannot figure out how my "ul" and "li" tags get rendered. I'm able to target them and apply colors for instance, but it still looks like a "p" tag on the page when viewing. Though if you inspect the element it does show "ul" and "li" tags.

See the example below:

Webpage with inspection

Here is my gatsby-node.js file:

const path = require("path")

exports.createPages = async ({ actions: { createPage }, graphql }) => {
  const { data } = await graphql(
    `
      {
        pages: allGraphCmsPage {
          nodes {
            id
            content {
              markdownNode {
                childMdx {
                  body
                }
              }
            }
            seo {
              description
              image {
                url
              }
              keywords
              title
            }
            slug
            subtitle
            title
          }
        }
        posts: allGraphCmsPost(sort: { fields: date, order: ASC }) {
          edges {
            nextPost: next {
              slug
              title
            }
            page: node {
              id
              author {
                id
                name
                title
              }
              content {
                markdownNode {
                  childMdx {
                    body
                  }
                }
              }
              date: formattedDate
              excerpt
              seo {
                description
                image {
                  url
                }
                keywords
                title
              }
              slug
              title
            }
            previousPost: previous {
              slug
              title
            }
          }
        }
      }
    `
  )

  if (data.errors) throw data.errors

  data.posts.edges.forEach(({ nextPost, page, previousPost }) => {
    createPage({
      component: path.resolve("./src/templates/article-post.tsx"),
      context: {
        id: page.id,
        page,
        previousPost,
        nextPost,
      },
      path: `/articles/${page.slug}`,
    })
  })

  data.pages.nodes.forEach(page => {
    createPage({
      component: path.resolve("./src/templates/default-page.tsx"),
      context: {
        page,
      },
      path: `/${page.slug}`,
    })
  })
}

exports.createResolvers = ({ createResolvers }) => {
  const resolvers = {
    GraphCMS_Post: {
      formattedDate: {
        type: "String",
        resolve: source => {
          const date = new Date(source.date)

          return new Intl.DateTimeFormat("en-US", {
            weekday: "long",
            year: "numeric",
            month: "long",
            day: "numeric",
          }).format(date)
        },
      },
    },
  }

  createResolvers(resolvers)
}

Here is my gatsby-config.js file:

require("dotenv").config()

const path = require("path")

module.exports = {
  siteMetadata: {
    title: `Code Shape`,
    description: `Learn to create great things.`,
    author: `@codeshape`,
  },
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/static/images/icons`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/static/images/logos`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/static/images/profiles`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `posts`,
        path: `${__dirname}/src/posts/`,
      },
    },
    {
      resolve: "gatsby-plugin-page-creator",
      options: {
        path: `${__dirname}/src/posts`,
      },
    },
    `gatsby-plugin-mdx`,
    {
      resolve: "gatsby-source-graphcms",
      options: {
        endpoint: process.env.GRAPHCMS_ENDPOINT,
        token: process.env.GRAPHCMS_TOKEN,
        buildMarkdownNodes: true,
        downloadLocalImages: true,
      },
    },
    `gatsby-plugin-styled-components`,
    `gatsby-plugin-react-helmet`,
    "gatsby-transformer-sharp",
    "gatsby-plugin-sharp",
  ],
}

Here is my gatsby-browser.js file:

import React from "react"
import { MDXProvider } from "@mdx-js/react"
import Layout from "./src/components/layout/layout"

/* eslint-disable */
const components = {
  wrapper: ({ children }) => <>{children}</>,
  h1: props => (
    <h1 style={{ fontSize: "60px", fontWeight: "bold" }} {...props} />
  ),
  h2: props => (
    <h2 style={{ fontSize: "40px", fontWeight: "bold" }} {...props} />
  ),
  h3: props => (
    <h3 style={{ fontSize: "30px", fontWeight: "bold" }} {...props} />
  ),
  h4: props => (
    <h4 style={{ fontSize: "20px", fontWeight: "bold" }} {...props} />
  ),
  p: props => <p style={{ fontSize: "16px", lineHeight: 1.6 }} {...props} />,
  strong: props => (
    <strong style={{ fontWeight: "900", lineHeight: 1.6 }} {...props} />
  ),
  img: props => <img style={{ width: "100%" }} {...props} />,
  pre: props => {
    const className = props.children.props.className || ""
    const matches = className.match(/language-(?<lang>.*)/)
    return (
      <Highlight
        {...defaultProps}
        code={props.children.props.children}
        language={
          matches && matches.groups && matches.groups.lang
            ? matches.groups.lang
            : ""
        }
      >
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    )
  },
}

const wrapPageElement = ({ element, props }) => (
  <Layout {...props}>{element}</Layout>
)

const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
)

export { wrapPageElement, wrapRootElement }

And here is the template for my article-post.tsx file:

import React from "react"
import { graphql, Link } from "gatsby"
import Img from "gatsby-image"
import { MDXRenderer } from "gatsby-plugin-mdx"
import styled from "styled-components"
import {
  BodyMain,
  Caption,
  H1,
  MediumText,
} from "../components/styles/TextStyles"
import ReactDisqusComments from "react-disqus-comments"
import LazyLoad from "react-lazy-load"

export const pageQuery = graphql`
  fragment AssetFields on GraphCMS_Asset {
    id
    localFile {
      childImageSharp {
        fluid(maxWidth: 600) {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }

  query ArticlePostQuery($id: String!) {
    authorImage: graphCmsAsset(
      authorAvatar: {
        elemMatch: { posts: { elemMatch: { id: { in: [$id] } } } }
      }
    ) {
      ...AssetFields
    }
    coverImage: graphCmsAsset(
      coverImagePost: { elemMatch: { id: { eq: $id } } }
    ) {
      ...AssetFields
    }
  }
`

export default function ArticlePostTemplate({
  data: { authorImage, coverImage },
  pageContext: { nextPost, page, previousPost },
}) {
  return (
    <Wrapper>
      <Header>
        <PublishDate>{page.date}</PublishDate>
        <PageTitle>{page.title}</PageTitle>
      </Header>
      <InformationWrapper>
        <AuthorWrapper>
          <AuthorAvatar>
            <Img
              fluid={authorImage.localFile.childImageSharp.fluid}
              fadeIn={false}
              className="Image"
            />
          </AuthorAvatar>
          <AuthorTextWrapper>
            <AuthorName>{page.author.name}</AuthorName>
            <AuthorTitle>{page.author.title}</AuthorTitle>
          </AuthorTextWrapper>
        </AuthorWrapper>
      </InformationWrapper>
      <ContentWrapper>
        <Img
          fluid={coverImage.localFile.childImageSharp.fluid}
          fadeIn={false}
          className="CoverImage"
        />
        <MDXRenderer>{page.content.markdownNode.childMdx.body}</MDXRenderer>
      </ContentWrapper>
      <Navigation>
        {(nextPost || previousPost) && (
          <div>
            <hr className="Divider" />
            {nextPost && (
              <div>
                <h2>Next Post</h2>
                <div>
                  <Link to={`/articles/${nextPost.slug}`}>
                    {nextPost.title}
                  </Link>
                </div>
                <hr className="Divider" />
              </div>
            )}
            {previousPost && (
              <div>
                <h2>Previous Post</h2>
                <div>
                  <Link to={`/articles/${previousPost.slug}`}>
                    {previousPost.title}
                  </Link>
                </div>
                <hr className="Divider" />
              </div>
            )}
          </div>
        )}
        <div>
          <Link to="/articles/" className="">
            &larr; Back to the blog
          </Link>
        </div>
      </Navigation>
      <LazyLoad offsetTop={400}>
        <ReactDisqusComments
          shortname="codeshape"
          identifier={page.id}
          title={page.title}
          url={page.url}
          category_id={page.category_id}
        />
      </LazyLoad>
    </Wrapper>
  )
}

const Wrapper = styled.div`
  margin: 1.875rem;
  display: grid;
  grid-gap: 1.875rem;
`

const Header = styled.div`
  text-align: center;
  margin: 0 auto;
`

const PublishDate = styled(BodyMain)`
  color: #757372;
`

const PageTitle = styled(H1)``

const InformationWrapper = styled.div`
  display: grid;
  align-items: center;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
`

const ContentWrapper = styled.div`
  display: grid;
  grid-gap: 1rem;
  margin: 0 auto;
  max-width: 80rem;

  .CoverImage {
    border-radius: 1.25rem;
    object-fit: cover;
  }
`

const AuthorWrapper = styled.div`
  margin: 0 auto;
  padding: 1rem;
  display: grid;
  grid-gap: 1rem;
  grid-template-columns: auto auto;
  width: 20rem;
  justify-content: center;
  align-content: center;
`

const AuthorAvatar = styled.div`
  .Image {
    border-radius: 50%;
    border: 3px solid white;
    width: 4rem;
  }
`

const AuthorTextWrapper = styled.div`
  display: grid;
  justify-content: center;
  align-content: center;
`

const AuthorName = styled(MediumText)`
  font-weight: 900;
`

const AuthorTitle = styled(Caption)`
  color: #757372;
`

const Navigation = styled.div`
  margin: 0 auto;
  text-align: center;
  padding: 1.875rem;
  display: grid;
  grid-gap: 1rem;
  width: 18rem;

  .Divider {
    color: #757372;
    line-height: 3rem;
  }
`

Dependencies in package.json file:

    "@mdx-js/mdx": "^1.6.22",
    "@mdx-js/react": "^1.6.22",
    "@types/react-helmet": "^6.1.0",
    "@types/styled-components": "^5.1.4",
    "autoprefixer": "^10.2.3",
    "babel-plugin-styled-components": "^1.12.0",
    "gatsby": "^2.28.0",
    "gatsby-image": "^2.10.0",
    "gatsby-plugin-manifest": "^2.8.0",
    "gatsby-plugin-mdx": "^1.9.0",
    "gatsby-plugin-offline": "^3.6.0",
    "gatsby-plugin-react-helmet": "^3.6.0",
    "gatsby-plugin-sharp": "^2.13.4",
    "gatsby-plugin-styled-components": "^3.9.0",
    "gatsby-source-filesystem": "^2.7.0",
    "gatsby-source-graphcms": "^2.2.0",
    "gatsby-transformer-sharp": "^2.11.0",
    "prop-types": "^15.7.2",
    "react": "^16.14.0",
    "react-disqus-comments": "^1.4.0",
    "react-dom": "^16.14.0",
    "react-helmet": "^6.1.0",
    "react-lazy-load": "^3.1.13",
    "styled-components": "^5.2.1"
  },

Can you help me with what I need to do in order for MDXrenderer to work? I still need to figure out how lists and code blocks get rendered.

1

There are 1 answers

2
Ferran Buireu On BEST ANSWER

You are overkilling completely the issue. You don't need to create an instance in the gatsby-browser.js to "make the MDX understand". Your MDX is working perfectly since you are rendering an <ul> and <li>. You are only missing the styles.

In the screenshot, you've shared, can be seen the following:

Inherited from ul
ol, ul {
   list-style: none;
}

This is making your lists to "looks like a <p>".

You just need to add the correct styles to them or removing the resetting styles. By default, the <ul> has:

display: block; 
list-style-type: disc; 
margin-top: 1em; 
margin-bottom: 1 em; 
margin-left: 0; 
margin-right: 0; 
padding-left: 40px;

And the <li>:

display: list-item; 

Once you restored the default styling, your list tags will look as they are.