1. Introduction to ReactJS and Documentation
How does ReactJS work? Hello, my name is Wojciech Miksiu and today I'm going to talk about documentation. My job is building React component library called baseweb and other webtools at Uber. Baseweb is an implementation of our design system and we use it across all applications. So let's talk about documentation. It's something that most of us use every single day and it's absolutely essential. Every day we are sitting at our desks trying to learn things by reading and only sometimes we pause to write a few lines of code. And there are many different sources of documentation. We have books, MDN, Stack Overflow, comments in the code or we just randomly Google things until we give up and slack our colleagues.
But what is my real motivation to talk about this today? I joined the company two years ago, and this was the proposition given to me. Join us. You'll be building React components. That sounded amazing and I was immediately sold. However, the reality of platform oriented work is slightly different. It looks more like this chart. And frankly, it's a good thing. If nobody uses your things, it means if nobody asks you questions, it means nobody uses your stuff and it would be awful. So you could say my team became a victim of its own success. When we started BaseWeb two years ago, it was barely used and we had a lot of time to just write code, but then the usage spiked. There is now over 600 applications just in Uber and that translates into almost like thousand developers. Also, our library has a giant API surface. There are over 60 components, hundreds of preps and pretty much endless possibilities. We also have a lot of outside contributors and users. So naturally, we spend a lot of time with support. The question was, can we reclaim some of this time back? I strongly believe that if we build an amazing documentation, we can significantly reduce the number of questions in our chats and GitHub issues. Also, we do frequent surveys. And documentation is always the most mentioned topic.
2. Importance of Documentation and Custom Website
My team invests time in experimenting with different kinds of documentation. I'll show you older versions of our documentation website and explain why it wasn't good enough. We launched our own custom website based on Next.js and MDX. Some static types are too complicated and not human-readable. Adding more examples can be overwhelming. We built a playground for our components to display and explore them.
So my team now invests a lot of time into experimenting with different kinds of documentation. Hopefully, by now, I made my case for why documentation is so important. Here's the agenda for the talk. I'll show you some older versions of our documentation website and explain why it wasn't good enough. Then what we built with react to you and use today. I'm also going to peek under the hood and show you some concepts and code related to compilers and abstract syntax trees. And finally, conclusion.
So this is the oldest version of our documentation, it's just storybook. It's off the shelf solution we used it to develop our components, but also to document them. You can see it's pretty much a bunch of examples, and at the bottom you can see read me where we document our props. But as we grew the number of components, we wanted to have something more structured and customized. So we launched our own custom website based on Next.js and MDX. Now components are in different categories, the page itself is more structured. Each example has its own source code that's manually written, and at the bottom we have a perp documentation generated based on static types. And this works reasonably well, until it just doesn't. Some static types are just too complicated and not really human readable. Also many props are meant to be used internally only, when composed by other components. But now we expose them in the public API, and that's just confusing. We tried to tweak this many, many times, but it was never reliable, and not sufficient as the primary source of documentation. Developers can always tell when something is auto-generated, and they will not exactly love it. Why should they put extra effort into reading it, if you put zero effort into creating it? So, how about adding a lot of examples? Who doesn't like more examples? This is our tech component. It's pretty simple, and yet, there are 384 different permutations of it. Can we display 384 examples on a single page? Sure. Is it going to be overwhelming? Absolutely. And the same thing goes for other components. If there is a limited number of examples we can present to not overwhelm our users, what else can we do? Well, let's un-toggle and explore these permutations. Let me show what we built.
So, this is like a playingground we built for our components. First of all, you want to display the component itself. The second most important part is probably the source code itself.
3. Editing Source Code and JSX Compilation
The nice thing about the source code is you can edit it in real time. And as you type, it changes the component. If you make a syntax error, we just tell you what's wrong. We also added this feature where you can hover over different components and see their static types. This is a pretty common feature on a lot of documentations.
4. Interacting with NAPs and Extracting Information
You can interact with the NAPs, which represent each prop of the component. The state is synchronized across all parts, allowing you to copy and paste the exact component in its current state. Additionally, you can still change the source code and see the updates reflected in the state. To describe the properties of the button, a simple configuration format is used. Extracting information from the code is done by turning it into an abstract syntax tree, providing a more powerful approach.
You can get back into the initial state. And you can also spin up code sandbox and do even more serious editing, if you wish. So, this is great, but sometimes you don't even know what other props you can use for this component. So, you need some sort of props API or props documentation.
So, we added this additional part we call NAPs. Each prop is represented by a single NAP, and you can also interact with it. So, as I change this value, you can see it updates the component itself. But most importantly, it also updates the source code. So, this state is synchronized across all of these three parts. And it gets even better, for example, if I change this kind to secondary, you can see it even changes the import. So, this is like something you can always copy and paste into your application and you can get this exact component in this exact state.
So, not only I can interact with these snaps, I can also still change the source code itself. And you can see, again, it updates the state here, so this checkbox is now checked, and the component is in loading state. So, how does this work? First of all, we need to somehow describe what properties our button has. So, we can render the naps. We've created a simple configuration format. Here's the example. For each prop, you can set the name, type, and description.
And, when we update the code, we now somehow need to extract some pieces of information out of it. We need the prop names and values so we can display them in our naps. The first quick solution would be to use a regular expression, like here. Here we are matching a JSX element and getting back the parts with props and children. It works, but it's extremely brittle. What if we have multiple components? What if they are nested? How to tell them each apart? Now the code is just one dumb long string. We don't have some deeper understanding of what it really represents. So we can't really do much about it. We need a completely different and more powerful approach. We need to turn our code into a tree. The abstract syntax tree. Fortunately, we already have a tool just for that in our project.
5. Understanding Babel and AST Explorer
Babel creates an AST out of the code you pass to it. AST Explorer is a useful tool to inspect ASTs. It helps identify the different parts of the code and their corresponding nodes, such as JSX identifiers and string literals.
It's Babel. In order for Babel to do anything with your code, it also needs this deeper insight into its meaning. So it always starts by creating an AST out of code you pass to it. You can inspect ASTs in this very useful tool, called AST Explorer. So here's our button. We are using Babel Parser. And on the right side, you can see the three representations of our code. Now if I hover over different parts of our code, you can see what node is being highlighted. So, for example, this button now corresponds to this JSX identifier. If I go over the type, you can see it's just JSX attribute and the primary is a string literal. So now I know exactly what each part of this code is and I can just be sure I'm getting values I need to get.
6. Building and Generating Code with AST
Here's the implementation: parse your code with Babel parser to generate the tree structure. Use the traverse function to find gsx attributes or props and save their names and values. We can also reverse this process by changing the props and updating the code. However, gluing strings together can lead to complications and syntax errors. Building an abstract syntax tree (AST) from scratch provides a more reliable approach. Once we have the AST, we can use Babel generator to turn it into code. To make the code more readable, we can use Prettier, a popular tool that uses the same AST representation as Babel.
So here's the implementation. First, you use the parse function from Babel parser and you give it your code, and this actually generates the tree structure you saw. And then we use traverse function to walk this tree and we are looking for all gsx attributes or props. And once we get this type of note, we just save the name and the value. So this is like a very simple way how we can get all the prop names and values from our component.
I also demonstrated we can do it in reverse. So I was able to change these snaps and it was actually updating this code. And by the way, we've never written a single line of this code in our examples. Like this is all generated just based on the prop configuration and the state of this playground. So how this like process works in reverse? So how this like process works in reverse? How do we actually generate a code? So as you can imagine, there is some sort of state that props where you have the list of the names and its values, you know what the name of component is. So again, we could try something simple and we could just glue a bunch of strings together and this will give us the final snippet and it works. It gets complicated pretty quickly with all the exceptions and different cases. And it's also a very easy way how to produce a code that doesn't work and has some syntax errors. We already learned similar lesson with regular expressions. And so again, we need more reliable approach.
I mentioned trees already and it turns out you can also just build them from scratch. So this is a simplified example of function that creates an AST of JSX element. We can compose nodes together to build our own tree from nothing. Since all these functions are statically typed, if we are forgetting to pass any value, we will get an error. And now we can be sure that whatever is the final script is, it's going to work. So this is, of course, now we have the AST, but we still need to turn it into the code. So once we have the AST, we can use generate function from Babel generator. And this just turns the AST into code. But there's one glitch. This will result only into single line of the code, which is not super readable. So we're gonna get fancier and we can use like other popular tool called Prettier. And Prettier is actually using the same AST representation for the code as Babel. So we can just use Prettier to print out much nicer code snippet. So there are many other features of this playground. I don't have time to show you.
7. Final Thoughts on Documentation and ReactVue
Not all components are as simple as a button. We support various complex scenarios, such as using local states, nesting multiple components, and building custom prototypes. React View is an open-source library with its own interactive documentation. Documentation is immensely important for both customers and developers. Abstract syntax trees and tools like Babel and Prettier can greatly improve documentation. Check out ReactVue, an open-source project that powers our examples and documentation. Having a prop specification for each component can also be translated into VS Code snippets, making it easier for developers to produce new code.
For example, not all components are as simple as button. Sometimes you need to include... Sometimes you need to use local states to make your examples realistic. We support that. Sometimes you need to nest multiple components. We support that. Or you even want to build a custom prototype and a custom NAP for it. We also support that. All this is prepped in this library called React View. It's open source. And it has its own interactive documentation. So, please check it out.
And here are some final thoughts. First, documentation is immensely important and a critical part of anything you ship. It helps your customers to use your product. But do it also for your own selfish reasons. You can save yourself a lot of time by not answering questions that can be answered by quality documentation.
Second, how can we make documentation much better? Get your superpowers with abstract syntax trees and tools like Babel and Prettier. We already use them to build our applications, but we can also use them to write some boring parts of our code and build much better documentation.
And finally, to get started, check out ReactVue. It's open source, and it powers all examples in my talk and our documentation. And there is one more thing, we found another use for having a prop specification for each component. We translated it into VS Code snippets. It's a great power user feature. Oftentimes, your developers don't even need to read documentation to produce new code. And what's better than that? If you want to keep up to date, here's my Twitter. And thank you for watching. Fantastic talks today, and yours definitely added to the long list of great talks that we've seen. And again, I just – the quote. Ah, so good.
8. Getting Started with Documentation and ReactView
Documentation matters. Start incrementally with Storybook to develop components and use its add-ons for documentation. If you need more, try a custom-based solution like Gatsby and MDX. ReactView can be used with Storybook as a complement. It generates source code and supports any React component. The process for our company involved building the component library using Storybooks.
This is like, yes, documentation matters, people. One question that I was thinking about when I saw this, and I remembered how I started with documentation back in the day. How would you say – what is a good way for people to get going with creating documentation? What's the good starting point? Yeah, so for us, it was also like a process, right? It took us a while to just build the whole thing, so I would say, start incrementally. I think Storybook is a great option, because you can use it to develop your components, and it has some really cool add-ons, so you can also document them. After a while, when that's not enough, or you feel you need something more, maybe you should use a custom-based solution. We had a really nice talk about Gatsby and MDX. I really, really like MDX. We use it as well. We use Next.js – it's similar. But yeah, just start incrementally. Storybook is a great way how to get started quickly. MDX is amazing after.
That actually brings me to a question from Forzu from the channel, which is, if I do have a Storybook showcase library, should I try out ReactView for the documentation? Our design system is pretty integrated with NX and Storybook. Is it a complement or an alternative to Storybook to use ReactView? You can use it with Storybook as well. ReactView is just a React component, so it can be used in Storybook. In some ways, it's similar. In Storybook, you also have these maps, where you can change values and just see different variants of your components. ReactView goes a step further. It generates the source code for you. So, yeah, you can use it at the same time. Storybook supports anything. You can just put there any React component. ReactView is just another React component, so you can definitely use it together. That is nice. That is pretty cool.
And you mentioned, coming back to the original question on how to get started, you mentioned that it was a process for your company. So what would you say was the process like? What were the steps that you had to go through? Besides picking your technology and picking a solution to produce the documentation, what did the full process more or less look like? Right. So first we just started building the whole component library. That was the priority, just to get all the components out. That's why we used Storybooks.
9. Building Custom Documentation and Search
We didn't spend much time on building custom documentation. We listened to user struggles and tried to answer their questions. To improve documentation, ask users for feedback and conduct surveys. Building a custom documentation allows addressing auxiliary and frequent questions. A good search feature is essential, and open-source solutions like Algolia can help. Elasticsearch is another option.
We didn't spend that much time on building custom documentation. But then we also have this support channel, and we were listening to people. They have a lot of struggles with using our components, and we were always trying to answer these questions. And then we see people are asking the same type of questions. We were thinking, okay, how we could bring these questions together and just solve them in our documentation so people stop asking us the same thing again and again.
So try to ask people that use your documentation, your project, what they think about your documentation, what is missing. They will definitely tell you because they spend a lot of time just searching for answers. So it's kind of like this constant feedback from your users that actually keeps improving your documentation. So you should frequently ask them, maybe do a survey. That is really cool.
Also, I was wondering about this. So you built a component library and I'm pretty sure that you have lots of components, right? And not just that. I mean, the components are one thing that people might have questions about. But there might be like auxiliary questions or frequent questions that you get asked that are not necessarily straight related to one component, but like the interaction of components or something like that. How can you make sure that users find the answers to their question within your large body of documentation?
Yeah, that's a great question. So that was also one of the reasons why we decided to build a custom documentation, not just using storybook. Because it's not enough just to document components, but you also want to have the story around the whole system. But just like covers like some other big questions. So, also, one thing how to make this easier is having a good search in your documentation. Like having a good full text search is really nice. And there are some open-source free solutions like Algolia, which are really nice that just build the index for you. Nice. Yeah. That's pretty cool. I think like there's also, what's it called, Solar and – ah, there's another one that I can't – Elasticsearch. No, that's a different thing. Is Elastic a full text search – Elasticsearch is a full – yeah. Yeah, so that would work as well. I haven't actually tried Algolia much. Also – excuse me.
10. Starting Documentation and Keeping it in Sync
When to start writing documentation depends on the project. For new prototypes, it's best to avoid documentation as APIs may change frequently. However, for long-term projects, it's easier to start documenting from the beginning. Avoid relying too much on automatic generation and focus on crafting human-readable documentation. Keep the documentation and code in the same place to ensure they stay in sync.
Also, another thing that I've seen with companies that did eventually invest in documentation, some of them did it a lot too late. Like if you have a huge surface to cover, then creating documentation very late in the process is really, really hard to do well. Would you say that you should write documentation from the get-go, from the beginning of a project, or does it depend on when to start writing documentation?
That's a good question, too. So, if you're building something completely new and you're just prototyping, it might not be the best to start with documentation. Because you will probably change APIs a lot, and you'll be just wasting a lot of time. On the other hand, if you know that the thing you are building is for the long-term, it might be much easier just to do it at the same time, like, start documenting everything at the same time. Because, you know, when you want to ship a project, you have no documentation. It's like really, really annoying. Like now, I have to stop doing anything and for the next two weeks, I'm just going to type a massive block of text. So, it's probably easier just to do it at the same time, unless you are really like prototyping something new, you are not sure about it yet. So, it's a kind of a compromise.
That's a good point. Yeah, that makes sense. Riffing off of that question, Fauzul is asking, do we have do's and don'ts like we have just seen in the last MDX talk? Probably. I would really like to showcase use cases like IKEA rooms for my component in the documentation. Also, great talk, he says. Thanks.
All right, so one don't is relying too much on automatic generation. It's very tempting to use some sort of tool that extracts static types out of your components and just generates this whole API documentation. I'm not saying API documentation is not useful, but people are already using TypeScript, so they can see what APIs they can use. They are probably interested more in some deeper use cases, so you should always try to handcraft your documentation to real scenarios. So just don't rely on autogeneration. Try to actually write human-readable documentation, something you would like to see as a developer. So that would be my don't, and at the same time, also the do. Just spend time and make it a nice, human-readable... maybe even funny, right? We want to just have fun when reading documentation.
Also, when it comes to being a developer and writing documentation, another thing that I notice and it's also kind of a bad practice... I think the only thing that is worse than documentation is documentation that is outdated. How do you deal with code changing and documentation not catching up? So I think the biggest thing is to keep the documentation and the code itself in the same repo, in the same place. And just, you know, reusing things. So if I change a component, it's actually going to break examples in my documentation, so I also have to fix those. And also, you know, any feature I add into any component, at the same time I also have to create documentation for it. So if you keep these things together in the same place, being tied together, you are always forced to keep them in sync, which really helps. That's a cool way of addressing this. Awesome.