npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bacons/mdx

v0.4.0

Published

Universal MDX renderer for React Native

Downloads

9,225

Readme

@bacons/mdx

Build-time MDX for Expo apps and websites.

Add the package to your npm dependencies

yarn add @bacons/mdx

Setup

Add support for importing md and mdx files in your metro.config.js file.

metro.config.js

const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);

config.resolver.sourceExts.push("md", "mdx");

config.transformer.babelTransformerPath = require.resolve("./transformer.js");

module.exports = config;

Create a custom metro transformer. This is used to transform MDX files into JS + React Native before transpiling with Babel.

./transformer.js

const upstreamTransformer = require("@expo/metro-config/babel-transformer");
const MdxTransformer = require("@bacons/mdx/metro-transformer");

module.exports.transform = async (props) => {
  // Then pass it to the upstream transformer.
  return upstreamTransformer.transform(
    // Transpile MDX first.
    await MdxTransformer.transform(props)
  );
};

Usage

Create a markdown file:

./demo.mdx

import { CustomComponent } from './my-custom-component';

# Hello World

I **am** a _markdown_ file!

<CustomComponent />

This file can be imported and treated as a React component:

./App.js

import Demo from "./demo.mdx";

export default function App() {
  return <Demo />;
}

Custom components

By default, this package uses an incomplete set of universal React Native components for DOM elements. You may wish to improve the components, add more, or swap them out for your own.

import { Text } from "react-native";
import { MDXComponents } from "@bacons/mdx";

export default function App() {
  return (
    <Demo
      components={{
        h1: (props) => <h1 {...props} />,
        // Add custom components which can be used as JSX elements.
        RedText: (props) => <Text {...props} style={{ color: "red" }} />,
        // This can be used as `<RedText />` without the need to import it.
      }}
    />
  );
}

Now inside of your markdown file, you can use the custom components:

# Hello World

<RedText />

You can set the components for all children using the MDXComponents React context component.

import { Text } from "react-native";
import { MDXComponents } from "@bacons/mdx";

export default function App() {
  // Pass any HTML element as a key to the MDXComponents component.
  return (
    <MDXComponents
      components={{
        h1: (props) => <Text {...props} />,
        // Add custom components which can be used as JSX elements.
        RedText: (props) => <Text {...props} style={{ color: "red" }} />,
        // This can be used as `<RedText />` without the need to import it.
      }}
    >
      <Demo />
    </MDXComponents>
  );
}

Be sure to pass the style prop down to the component you're using, this is how the styles are cascaded.

Custom styles

This package works similarly to most docs sites. You create high-level styles for the entire site. This can be cascaded down to reduce the scope of a style.

import { MDXStyles } from "@bacons/mdx";

export default function App() {
  // Pass any HTML element as a key to the MDXStyles component.
  return (
    <MDXStyles
      h1={{
        fontSize: 32,
        fontWeight: "bold",
        color: "red",
      }}
    >
      <Demo />
    </MDXStyles>
  );
}

The <MDXStyles> components can be stacked in different levels, think of these like CSS classes.

Typescript

You can add support for importing .mdx files in your tsconfig.json file.

tsconfig.json

{
  "compilerOptions": {
    "typeRoots": ["./global.d.ts"]
  },
  "extends": "expo/tsconfig.base"
}

Now create a file that declares the module.

./global.d.ts

declare module "*.mdx" {
  import React from "react";
  import { CustomComponentsProp } from "@bacons/mdx";
  const Component: React.FC<{
    components?: CustomComponentsProp;
  }>;
  export default Component;
}

Experimental native errors

Optional native-only step, not required for MDX to work.

React Native has suboptimal error messages for when you use React DOM components on native or render strings outside of <Text /> elements. This can make migration and code sharing very painful. This package has an experimental dev-only feature to print out optimized errors when you render react-dom built-in's such as div, p, h1, etc. on native.

Simply add the following to your babel.config.js, and clear the transform cache npx expo start --clear:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [["babel-preset-expo", { jsxImportSource: "@bacons/mdx/jsx" }]],
  };
};

Now when you render a div, p, h1, etc. on native, you will get a helpful error message.

export default function App() {
  return <div>Hey</div>;
}
ERROR  Unsupported DOM <p /> at: /Users/evanbacon/Documents/GitHub/bacons/mdx/apps/demo/src/App.tsx:1:11
This will break in production.

Web-only components

It's possible to parse MDX to DOM elements instead of universal components. This can be useful when building for web-only or migrating from web-only. To do this, pull in the getDOMComponents function and pass it to the components prop of the MDX component.

import { getDOMComponents } from "@bacons/mdx";

import Demo from "./readme.md";

export default function App() {
  return <Demo components={getDOMComponents()} />;
}

This will render the following MDX as DOM elements:

# Hello World

I **am** a _markdown_ file!

And the DOM:

<h1>Hello World</h1>
<p>I <strong>am</strong> a <em>markdown</em> file!</p>

Next.js Usage

  1. Follow the steps detailed here: https://nextjs.org/docs/advanced-features/using-mdx
  2. Add the following packages to transpile-modules within your next.config.js:
    '@bacons/mdx',
    '@bacons/react-views',
    '@expo/html-elements',

Known Issues

  • ol, li, ul are all buggy. PRs welcome.
  • Native image ratios are weird.

About this project

This is a universal MDX implementation for Expo (React & Metro). It aims to be a general-purpose MDX implementation for Expo projects that leverage universal Metro (Expo CLI).

  • Test in the apps/demo project.

This library powers my (Evan Bacon) personal blog, the source can be found here: Evan Bacon portfolio.