Skip to content

Instantly share code, notes, and snippets.

@boganegru
Created July 19, 2020 11:28
Show Gist options
  • Save boganegru/a4da0b0da0b1233d30b10063b10efa8a to your computer and use it in GitHub Desktop.
Save boganegru/a4da0b0da0b1233d30b10063b10efa8a to your computer and use it in GitHub Desktop.
import React from 'react';
import ReactMarkdown from 'react-markdown';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Link from '@material-ui/core/Link';
import Table from "@material-ui/core/Table";
import TableContainer from "@material-ui/core/TableContainer";
import Paper from "@material-ui/core/Paper";
import {TableHead, TableRow, TableCell, TableBody} from "@material-ui/core";
const styles = (theme) => ({
listItem: {
marginTop: theme.spacing(1),
},
header: {
marginTop: theme.spacing(2),
}
});
function MarkdownParagraph(props) {
return <Typography>{props.children}</Typography>
}
const MarkdownHeading = withStyles(styles)(({ classes, ...props }) => {
let variant;
switch (props.level) {
case 1:
variant = "h5";
break;
case 2:
variant = "h6";
break;
case 3:
variant = "subtitle1";
break;
case 4:
variant = "subtitle2";
break;
default:
variant = "h6";
break;
}
return <Typography className={classes.header} gutterBottom variant={variant}>{props.children}</Typography>
});
const MarkdownListItem = withStyles(styles)(({ classes, ...props }) => {
return (
<li className={classes.listItem}>
<Typography component="span">{props.children}</Typography>
</li>
);
});
function MarkdownTable(props) {
return (
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">{props.children}</Table>
</TableContainer>
);
}
function MarkdownTableCell(props) {
return <TableCell><Typography>{props.children}</Typography></TableCell>
}
function MarkdownTableRow(props) {
return <TableRow>{props.children}</TableRow>
}
function MarkdownTableBody(props) {
return <TableBody>{props.children}</TableBody>
}
function MarkdownTableHead(props) {
return <TableHead>{props.children}</TableHead>
}
const renderers = {
heading: MarkdownHeading,
paragraph: MarkdownParagraph,
link: Link,
listItem: MarkdownListItem,
table: MarkdownTable,
tableHead: MarkdownTableHead,
tableBody: MarkdownTableBody,
tableRow: MarkdownTableRow,
tableCell: MarkdownTableCell,
};
export default function Markdown(props) {
return <ReactMarkdown escapeHtml={false} renderers={renderers} {...props} />;
}
Copy link

ghost commented Jul 14, 2023

Thank you very much!!

Copy link

ghost commented Jul 14, 2023

I updated this to use more modern versions of Material UI and (following @Nitzperetz 's change of using components):

Edit 1: better code highlighting support:

import React from "react";
import { default as ReactMarkdown } from "react-markdown";
import {
  Link,
  List,
  ListItem,
  ListItemText,
  Paper,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableRow,
  Typography,
  useTheme,
} from "@mui/material";
import remarkGfm from "remark-gfm";
import { default as SyntaxHighlighter } from "react-syntax-highlighter";
import {
  tomorrow as lightHighlightStyle,
  tomorrowNight as darkHighlightStyle,
} from "react-syntax-highlighter/dist/cjs/styles/hljs";
import GlobalStyles from "@mui/material/GlobalStyles";

export default function Markdown({ markdown }: { markdown: string; }) {

  const theme = useTheme();

  return (
    <ReactMarkdown
      remarkPlugins={[remarkGfm]}
      components={{
        // *********
        // * Links *
        // *********
        a: ({ href, title, children }) => (<Link href={href} underline={"always"}>{children}</Link>),

        // ********
        // * Text *
        // ********
        p: ({ children }) => (<Typography sx={{ mt: 1 }}>{children}</Typography>),
        del: ({ children }) => (<Typography sx={{ mt: 1, textDecoration: "line-through" }}>{children}</Typography>),
        em: ({ children }) => (<Typography sx={{ mt: 1, fontStyle: "italic" }}>{children}</Typography>),
        strong: ({ children }) => (<Typography sx={{ mt: 1, fontWeight: "bold" }}>{children}</Typography>),
        b: ({ children }) => (<Typography sx={{ mt: 1, fontWeight: "bold" }}>{children}</Typography>),
        h1: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h1"}>{children}</Typography>),
        h2: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h2"}>{children}</Typography>),
        h3: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h3"}>{children}</Typography>),
        h4: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h4"}>{children}</Typography>),
        h5: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h5"}>{children}</Typography>),
        h6: ({ children }) => (<Typography gutterBottom sx={{ mt: 2 }} variant={"h6"}>{children}</Typography>),

        // **********
        // * Tables *
        // **********
        table: ({ children }) => (<TableContainer component={Paper} sx={{ mt: 2 }}>
          <Table size="small">{children}</Table>
        </TableContainer>),
        tbody: ({ children }) => (<TableBody>{children}</TableBody>),
        // th: ({ children }) => (<TableHead>{children}</TableHead>),
        tr: ({ children }) => (<TableRow>{children}</TableRow>),
        td: ({ children }) => (<TableCell><Typography>{children}</Typography></TableCell>),

        // *********
        // * Lists *
        // *********
        ol: ({ children }) => (<List sx={{
          listStyleType: "decimal",
          mt: 2,
          pl: 2,
          "& .MuiListItem-root": {
            display: "list-item",
          },
        }}>{children}</List>),
        ul: ({ children }) => (<List sx={{
          listStyleType: "disc",
          mt: 2,
          pl: 2,
          "& .MuiListItem-root": {
            display: "list-item",
          },
        }}>{children}</List>),
        li: ({ children, ...props }) => (
          <ListItem sx={{ m: 0, p: 0, ml: 2 }} disableGutters><ListItemText
            sx={{ pl: 0.25 }}>{children}</ListItemText></ListItem>),

        // ********
        // * Code *
        // ********
        code: ({ node, inline, className, children, ...props }) => {
          const match = /language-(\w+)/.exec(className || "");
          return (
            <>
              <GlobalStyles styles={{ code: { color: "inherit", background: "transparent" } }} />
              <SyntaxHighlighter
                style={theme.palette.mode === "light" ? lightHighlightStyle : darkHighlightStyle}
                language={match ? match[1] : undefined}
                PreTag="div"

              >
                {String(children).replace(/\n$/, "")}
              </SyntaxHighlighter>
            </>
          );
        },
      }}
    >
      {markdown}
    </ReactMarkdown>
  );
}

@Nitzperetz
Copy link

@nbarrow-inspire-labs very cool! that looks the same as my implementation.

One note - often you would want the code to be inline, I think that use case was not handled in the code above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment