Building React Websites and Apps with Markdown - Guillermo Rauch @ ReactNYC

In this talk Guillermo Rauch, CEO and founder of Zeit, presents the open‑source framework Next.js and its MDX extension for building React‑based websites and applications with Markdown. He begins by introducing Zeit’s mission to simplify cloud computing and then showcases Next.js’s core capabilities: a pages‑based file system, automatic routing, Link component, and the “killer feature” of prerendering – supporting both static export (no‑flicker, SEO‑friendly HTML) and server‑side rendering for dynamic data. He shares metrics (stars, contributors, live apps) and recent improvements in version 6 such as reduced bundle size, faster boot, better page lookup, integration and security testing, and Flow‑based code conventions. Rauch then explains why pure JSX for content is cumbersome and introduces MDX – an enhanced Markdown format that compiles to React components. He walks through the evolution from inline Markdown‑in‑JS to importing components directly in .md files, the need for a component mapping (h1, code, blockquote, etc.), and the minimal webpack plugin that auto‑generates the MDX loader configuration. He demonstrates using Remark plugins (e.g., remark‑capitalize) to enforce style rules at compile time, and shows how custom React components (e.g., a Google Map) can be embedded in Markdown. The talk also covers the workflow benefits: content can be edited directly in GitHub as plain .md files, enabling low‑barrier contributions, while developers retain a component ecosystem. He discusses a more declarative approach using Web‑Component‑style slots (React Tunnels) to inject dynamic data (e.g., view counters) without embedding logic in the Markdown itself, preserving simplicity and allowing external state injection. Type‑checking with Flow is highlighted as a way to catch mistakes in the generated JSX. Finally, Rauch shares Zeit’s collaborative culture: decentralized teams, RFC‑driven specifications, and the preference for thoughtful, asynchronous communication over noisy chat. He demonstrates deploying a fully static export of a Next.js + MDX site with a single `now` command, illustrating the end‑to‑end pipeline from Markdown authoring to cloud deployment. The talk concludes with a brief Q&A about the declarative nature of MDX and the avoidance of stateful logic within Markdown files.
Chapters
My name is Guillermo Rauch, and I'm the CEO and founder of Zeit. You might have heard of it from some of our open source projects. The open source project that I'm going to talk about today is called Next.js, and it allows you to generate a React application very easily, kind of like create-react-app. But we have one killer feature, which is called prerendering. So whether you want to compile an application statically and prerender all your beautiful content and your UI so there are no flickers, or you want to server render so that you have dynamic content that search engines and users can access so that their first request gets all the data directly, you can use Next.js. So I'll give a brief introduction about our company. It's called Zeit, and you can go to our website, zeit.co. I'll switch quickly here. Zeit.co. So, we want to make developers' lives better. The mission of Zeit is to make cloud computing really accessible. So, any one of you here should be able to deploy to the cloud with one command, and you should be able to use tools that you enjoy and make your life easier. So, our medium is open source. We are very passionate about sharing everything that we learn. So, everything you're going to see today comes from us building our own website. And wanting to write content more easily, write content better, update it directly from the GitHub UI, for example, and also use React components in addition to writing our content easily with Markdown. So we launched our first open source project 2 years ago. And by the kind of arbitrary magic of GitHub stars, we're already the 50th largest organization on GitHub. Something that makes us very proud. 51st. Some of you might know me from my previous venture, Socket.io. It's still doing great, but we just passed them. Yes. Okay, so today I'm gonna talk about Next.js. And our tagline is React applications made easy. So it's a very general purpose tool that allows you to build applications and websites. So I like to show this little demo for those of you that are not directly familiar with Next.js of how you would go about starting your React application. So you would create your package.json file. You run npm start, which runs the command Next. Just that, that's all, Next. And then you create a page inside the pages directory that exports a stateless functional component or a class component. So notice that I'm creating here a— functional component. I export it. It's inside the pages directory. And as soon as I save this file, it'll magically appear on the left-hand side. So what's the next thing you need when you build an application or website? You need hyperlinks. And Next.js also makes that very easy. It just gives you a Link component. And why is it amazing? Because you don't change the way you write React applications. So we kind of built a very slim layer on top that allows you to link together your pages and your components. Other than that, it's just raw and adultered organic farm-to-table oat milk, almond milk React. Great. So Next.js in numbers. 25,000— actually, almost 26,000 stars. I sort of got to measure other things that aren't stars. I don't know why I'm so obsessed about stars. 400-plus contributors. Lots of releases, lots of commits, lots of forks. Not sure if this is good. Lots of pull requests. And we track, we inserted a GDPR uncompliant header. Just kidding, I know about GDPR. That shows the Next.js version and we crawl the internet and there is almost, this number is outdated. There's more than 10,000 live applications already out there. It's great. There's one Vue.js alternative for those that have sneaked in that love Vue.js. It's called Nuxt. So we recently launched Next.js 6.0 with a strong focus on making it slimmer, actually. So if you're familiar with the serverless world, there's a strong pressure for your applications to be smaller and boot up faster. So this is something that we're watching and taking care of very— Closely because we want Next to be very future-proof. We want Next.js to be very slim and not add a lot of bloat on top of what you're doing. So we reduce a lot the number of dependencies. We sped up boot up time, which is really nice for when you're doing server rendering. So your application can come back alive from sleeping very quickly. And we improved page lookup a lot. So as I showed in the beginning, you have a pages directory where you put your entry points. That also do code splitting of your React application. We improved page lookup times a lot. We're also doing a lot for integration testing and security testing. We've had a few path traversal vulnerabilities, actually one that was reported by HackerOne a few months ago. So we decided, okay, we need to take a look at this more closely, and we did a lot to have this really robust integration test suite. And we improved our code conventions via Flow recently. So one of the other things that's really neat about Next is that it allows you to run one command to export your application statically. So you have your pages directory that have all these entry points. You run next build and next export, and boom, you get a bunch of HTML files and a bunch of code-split entry points of JS bundles that make up your application. So in the past, for those of you that were familiar with Next, you used to have to declare a route map on your config. Not anymore. The only thing that you have to do to export your application statically is next build and next export. And if you're getting lost in this spaghetti of commands, I'm going to show you at the very end an example of combining static exports with Next and Markdown, which is the subject of today's talk. So JSX is really great. In the sense that you're basically just embedding your content directly into JavaScript. For the most part, for example, I wrote my blog, rauchg.com, as a Next.js application that also server renders and does all that fancy stuff. And I wrote it all using markdown— sorry, JSX. So just raw JSX. And if you want to see an example, you go to github.com/rauchg/blog. So if you go to pages, as I mentioned earlier, the pages file system structure determines your default URL structure, which is a really neat feature. So 2017, it's hard to forego efficiency. 2016 review. So let's go to 2016. Oh no, it's in 2017. Okay, GitHub. So 2016 review, and if you go to it, it exports obviously what? A React component. But what it did is it also used a ton of JSX for writing my actual post. And actually, it's kind of nice because I run Prettier on top and it's easy to edit. I didn't mind it personally, but my coworkers did. And the GitHub syntax highlighter team also did because it's not pretty. So it's actually a really neat example of using Next.js because all I had to do to start a blog was I created a pages directory. And I put my content in it. And then I define all my components and all that. It's quite beautiful. And then I actually have this like real-time view counter, which pulls my views for this blog post from Firebase, and then it keeps them updated in real time. So it's kind of a nice thing to show off this prerendering functionality that I was talking about. So we do so, the data fetching, by exposing a hook into React that allows you to fetch data, and it's called getInitialProps. You can, you can look into it later on nextjs.org. When you prerender something, you also want to activate your data pipeline. So if I'm going to a blog post, in this case my data pipeline is fetching the number of views that the blog post had. And there's a lot other examples of fetching data at the prerender time that are very useful. So if you want to dive deeper into starting your next React application with Next, just go to nextjs.org. We also have this very sped up video of building an entire PWA from scratch with Next. So it kind of walks you through a similar example of what I was walking you through earlier. As you can see, just we mix and match components and we link pages together and that's all there is to it. So JSX is great and it allows us to define very rich components. But when it comes to writing a lot of documentation and blog posts, some people prefer writing those in Markdown. And even though I was hesitant in the beginning, I kind of started to understand why people would want to do this over time. So an example of that is, first of all, when you clone a repository, we have the ability to just serve a bunch of Markdown files, .md files, and the barrier to entry to that repository is basically nothing. You don't even have to know— JavaScript, React, anything, you just go to that. So for today, we put together a little example of a repository using this technology that we call MDX. So what is MDX? MDX is a slight enhancement of Markdown to support React components natively. So as you can see here, I'm gonna go to the raw representation of this. So this is my .md file that, as you can see, inlines a React component directly. And this might not seem like such a big deal, but we went through several different iterations to arrive to this. So in the beginning, we used to inline Markdown directly inside our JS files, kind of like we do today with JSX. So we had a special template tag with a technology called Markdown in JS and would export a component that inlined Markdown inside. So this is kind of reminiscent of exporting a component that returns some JSX. But we still thought that the barrier entry to this was kind of high. So you have your JS file, and sure, you have your Markdown content inlined. And some other people do it through a Markdown JSX tag. But there's still a bit of a barrier to entry. For example, you couldn't really use code blocks because the template tag here doc would conflict with the code. And so you would have to escape special characters inside the template literal. So it was kind of messy. So once upon a time— and this is basically what my keynote talked about— I tweeted, what about— importing components— actually, this is great. Twitter can replace Mandara presentation. So what about importing things directly inside Markdown? So sort of turning Markdown into JSX or mixing and matching them. So I wrote a Spectrum post about that. It was kind of like a code golfing idea. Sort of, OK, is this a bad idea? Is this a good idea? And I was showing people how we were doing it at the time. Which was with Markdown in JS. I was saying, okay, you have to import your components and then you have to export. And I was just showing the escaping hell, let's call it, escaping hell of, for example, inlining a code snippet. So I enumerated some concerns that I had with that, like it's hard to lint it, it's hard to make it look nice. Let's just go through an imaginary demo. So the first commit that we made is basically bootstrapping the project. So one thing that I mentioned earlier was we can enhance Markdown with custom components. But you can also define, OK, these are my default components. Because the beauty of Markdown is that there are sensible defaults, right? So like a heading, a heading level 1 looks like a heading level 1. So how do we retain that simplicity? We can have a components mapping that says, OK, I want my h1 to look like this. So when you configure MDX, you can say these are my components that map to the default Markdown components. So h1, h2, h3, h4, inline code, which is like the backticks inside a paragraph, full code, block quote, et cetera. The other thing is— including webpack configuration. Because Next tries to make your life easier, instead of writing the webpack configuration directly, we can use this little— it's almost like a functional generator of webpack config. So you invoke that function and it sort of decorates the object that you pass in and it returns the webpack configuration for the MDX loader. So really what we're shooting for is that all that you would need to do to set up MDX is you export this plugin. And I say plugin with this gesture, plugin, because it doesn't do anything other than just sort of auto-generating the webpack config for you. And these plugins are very easy to write as well. So another interesting thing is, as far as dependencies, We just bring React and ReactDOM as peer dependencies, which gives you the freedom to sort of select your version, et cetera. We never lock you into a specific React version. Here we brought the plugin, which is really only necessary at dev time. So I think it could have been as a dev dependency in this case. And then again, these things try to be— not get in your way, so they tend to require peer dependencies. So the peer dependencies of the Next MDX plugin is bringing the MDX loader and the MDX core runtime. And then comes the fun part. So let's go to the link again. So I'm going to go to the pages directory. So as you can see, we have this pages directory with a bunch, uh, with a post subdirectory and a bunch of Markdown files inside. So if I go to any Markdown file here, you're going to see that it automatically mapped to my, in this case, JS file. But my JS file, all it's doing really, it's importing my Markdown component. So if you're familiar with JSX and how it sort of desugars to React.createElement calls, you can imagine that the Markdown— the MDX loader desugars your .md file to a JS file that exports a component that uses a bunch of other React components inside. So that's the beauty of how MDX is implemented. It literally knows about the entire— and I think literally was well used there, because it literally knows about the entire specification of Markdown. So you can say, this heading, we can transform it into— we can desugar it to a JSX element h1. And then we map— we auto-generate and import h1 from our components list. So it's very straightforward, really, of a transformation. Everything compiles down to a component, which is a building block of React. So here I have next and MDX.md. And when I was cloning the project, I was going to show you branch by branch or commit by commit. We started with this super basic component that just writes React with a heading and a subheading. And then over time, we actually enhanced these two examples with more goodies that MDX gives you. One of them is— the plugin ecosystem of MDX, which builds upon a tool called Remark. So in order to enhance your Markdown, all you have to do is bring in Remark plugins. So I'll give you an example of a Remark plugin here. So we have a guideline at Zeit that all our headings have to be properly capitalized according to the Chicago Manual of Style. I did not create that policy, but I like it. But what I didn't like was people complaining to me, oh, you didn't apply the blah blah blah capitalization thing. So a coworker of mine actually went as far as creating a website for it. So you can capitalize with style. So with doesn't get capitalized, completely arbitrary, but beautiful. So when we were looking at enhancing our Markdown-based workflow, we were like, OK, we can do all this awesome ahead-of-time compilation things, okay? Because we're processing the Markdown ahead of time, we're compiling it to JSX. A lot of web components that do Markdown things in many cases go as far as shipping a Markdown parser to the browser, whereas we take the side of doing as much work as possible at compile time so that your users just get the content, right? When we have this .md file, notice that the testing subheader is not capitalized. But when it actually gets compiled, we use the remark-capitalize plugin. So how does that happen? I'll show you really quick. So because we leveraged the remark ecosystem, we didn't have to reinvent a Markdown plugin ecosystem, which is really neat. Not all of them work super out of the box, but this one worked perfectly because I think we actually created it. But most plugins work really well. Some of them just don't have proper— yeah, I guess we created it. Sorry. But it's great. So the only thing you have to do is that when you have your Webpack configuration and you load the loader, you can give it an extra set of plugins. So in this case, we're giving it remark-capitalize. And I'm gonna show you the commit for that as well. So I'm gonna go to the root of my directory and next.config.js. So here we have, we're registering the plugin and we're passing the remark-capitalize plugin. So this shows you that you can do a lot of really awesome things to preserve the ease of writing Markdown because really it all comes down to you can edit Markdown way more easily than anything else in the universe. So it satisfies that restriction that I could go here and edit directly from GitHub. And it could send a documentation patch, can send in a new API description. But the neat thing, as I was mentioning earlier, is that you can leverage your existing component ecosystem. So here we're defining the existing components that I had, like all the h1, h2, et cetera. But I'm going to show you a— really cool example. I think it's this one. No, it's the other one using MDX. So the Google Map, we created a map component that uses the React Google Map library, and then we're just embedding it here. So earlier I mentioned that Markdown gives you a lot of sensible defaults. It also allows you to inline HTML directly, which we support. So we compiled non-capitalized HTML elements to regular JSX elements. But in this case, because it's the uppercase and because there is a matching import, we're inlining the actual Google Map super cool interactive component here. So if you're thinking about your content writing team and how to make their life easier, this is it. However, there's one extra nice thing we could do that we're working on right now. So I remember in the Spectrum thread where all this was born, which I think is a nice historical ref, someone mentioned that it'd be nice to separate actually the actual component rendering from the storage of Markdown so that the Markdown would be completely dumb, let's say. So we can still preserve that property, and it's sort of a trade-off of being extra declarative or being sort of magical. So one of the things we're experimenting with is using the web components syntax. So you would use something like this, Google Map. And then you wouldn't define your import, but you'd still have a component mapping configuration file where you can say Google Map matches this. And then you could store this even in a database. It doesn't matter where. So there's a little bit of a trade-off between— in this case, if you clone the repository, you can completely reproduce everything. You can find where the components are declared. You can edit them. You can hot code reload. But if you still wanna preserve a CMS type workflow, you could still do that because you could retain your CMS editing workflow for your content teams. And then you tell them, we have all these tags. And then when the tags get stored and then fetched by us, remap them again. And we can say, OK, the Google Dash Map element matches this component, and so on. So it's a slight improvement in the Markdown way of doing things. But really, we're giving you the— and it would get rid of maybe that. So it's a slight trade-off. If you want a complete declarative approach like this, or a more less strict mapping of components to raw elements. So the final thing of this example was I was going to clone it, and then I was going to run npm run build, which executes next build and next export, like I showed in the beginning of the presentation before the laptop died. And then what that does is it gives us an out directory. And our out directory contains the exported application. So every page becomes a directory, and every entry point becomes an HTML file. What's happening here in this example that I deploy to NOW is there is an HTML file there. So this is a completely static application. And all we had to do to deploy it was run one command, now. So if you want to learn more about how you would go about deploying such a static application, go to now.sh. So this example is again what I would have run on my own terminal. You go to directory, you run now, and you get a hyperlink that looks like this. That's it. Do you have any questions? I just wondering about what it's like to manage or work in a decentralized team. I think Site is decentralized. Very decentralized. I mean, so much so that I've never met this person for Pyne. So, I think actually this is— and he doesn't even work for my company, he works for Clearbit. So, this is I think kind of the workflow that we want to create. In fact, we've been taking this path of documenting specifications and RFCs within our team before we even build anything. And it's been super successful. As I said, it actually surprised me that from initial tweet to create, to a specification, 3 days passed. So I think in order to scale a team that is working on so many different complex challenges, this kind of transparency and this kind of specification-oriented approach with conversation is super, super useful. So I think what's happened traditionally, and I think I love Slack and we use it a lot, is that most things start with a conversation, and then maybe this actually never happens. So I think it should be the other way. And over time we've actually implemented a lot of soft policies of let's move all the challenging stuff to GitHub issues that you spend quite a bit of time thinking about and writing. In fact, the React team and a lot of people that have worked at Facebook and then have come to open source continue to use this model of using their internal groups. I think the asynchronous, thoughtful, long message-driven communication approach sometimes is better than the maniac GIF emoji, just one word per sentence, enter. That's what I do. I think it's better than the Slack approach. But I think both are complementary. So I think this is an example of something that works really well for us. We write RFCs and then we get critiques.
I really like MDX. I'm wondering, I noticed that you are going towards very declarative. And this is the first time I heard about the web components idea, which is interesting. Do you ever see any logic or state in MDX itself?
We try to avoid it. For the most part, I think it's been a really healthy pressure towards making super simple components, super straightforward pages. 'Cause when you start with just Markdown, you don't have as much of a temptation to include all this complex set of components and statefulness and all that. We actually, by design, kind of took away the possibility— I mentioned here that we could use slots. So what I wanted to avoid was the idea of passing parameters down because I thought that it would kind of force you to go more in that direction and take away from the simplicity of writing Markdown. But there is one interesting way that you can sort of introduce a lot of dynamic data without taking away from the ability to write stuff that is very easy to write. Which is using this idea of slots. So I think I mentioned it here. Oh no, no, I mentioned it as a reply to myself. Yeah, so Javi Velasco, who works with us, had written this application called React Tunnels. So it kind of works like Web Components slots. So a lot of the ideas in Web Components are actually really great, by the way. So the idea is that you cannot define placeholder elements. That have nothing, and then they get populated by the outside. So let's go back to my example of I wanted to include a dynamic count of views. One way that you can do so with Markdown would be here where you say an h1 tag, you could say an h1 tag slot. And then, or in the case of the way that React Titles works, you would give it an ID. Slot ID views. So if you rendered it naively, it would be a noop. So the slot would be empty. But if you render it with something that provides for it, it works like new React context basically. And you just populate it externally. So I think it's a good balance between sort of maintaining this idea of like Markdown is super easy to write and actually creating an artificial barrier between the people that are super focused on the content and the people that are focused more on the implementation and UI guidelines of that content. So I think it's a nice purposeful abstraction and division between the state and data propagation and super easy to write Markdown. That's why I was saying that this is a very nice thing to do if you wanna be super declarative, but you also have the ability to override elements externally so that it's just the content. The downside, and this is something we're exploring right now, is people still need utilities to lint, people still need utilities to type check, right? You don't want to isMarkerShown to miss the n and nothing complains until you get to compile time. So the nice thing is you can still do all your compilation logic because this desugars to JSX. You can then run the JSX through Flow compiler And Flow Compiler would tell you, hey, isMarkerShow without the end doesn't exist. But that's the current space that we're exploring, OK? How we can make this as simple as possible, but not lose and miss out on all the nice things of a strict Flow-aided, for example, JavaScript, which will complain to you about making mistakes.
More from Guillermo

Guillermo Rauch at Founders in Arms Live: Simplicity, Focus, and the Bet That Built Vercel

The Man Who Built a $9B Dev Platform Thinks You're Overcomplicating AI | Guillermo Rauch

Inside v0: How Guillermo Rauch Is Building 100 Million Engineers | Lenny's Podcast - EP.89

Architecting for model step-changes: A fireside with Vercel's Guillermo Rauch

Keynote Interview: Vercel's Guillermo Rauch on the Agent Era | Azure Cosmos DB Conf 2026

Vercel CEO: 70% of Our Traffic Is Now AI Agents "Nobody Was Prepared" | Anthropic, OpenClaw, OpenAI

Why being "terminally online" is more important than ever, with Vercel CEO Guillermo Rauch

Argentina Week 2026 | Argentine Talent Powering Global AI | Guillermo Rauch & Hernán Kazah


