Editor focus: Tables, cards, and our ongoing commitment to making GitBook the best docs editor
Editor focus: Tables, cards, and our ongoing commitment to making GitBook the best docs editor
Product updates
Product updates
Product updates
25 Apr
Recently we’ve been putting a lot of focus on honing published documentation to give your users the very best experience when they visit your docs. But while that is at the core of our product, so is the editing experience within the GitBook app. Especially for you folks who use it every day.
We think our editor is already pretty great, but there’s always room for improvement. And we know that if a documentation platform’s editing experience sucks, nobody is going to enjoy using it. Anything that makes it harder to edit your docs is a barrier to keeping your docs up to date, and that’s never a good thing.
That’s why we’re putting in a consistent effort to improve our editor, now and going forward. Over the last few weeks we’ve shipped a ton of stuff — you can check out our recent blog post to read about most of them, or head to the changelog to learn about them all in even more detail.
But I wanted to dive into some of these improvements in some more detail today, as an example of the work that’s been happening behind the scenes.
Building better Tables
Tables are pretty complex elements. They need to support a lot of interactions and data entry options — including reordering columns and rows, setting the column widths, navigating table cells using the keyboard, and much more.
Tables in GitBook were written with react-native web, which means a number of abstractions — especially on the level of event handling. Since the table editor is a very interaction-heavy block, it’s no surprise that there were some issues.
To overcome these issues with reordering, resizing, navigation, selection, and the general UX, I realized the best solution was to completely rewrite the block to use modern React.
Let’s take a closer look at some of the table’s inner workings and how it affected some of the features we’ve worked on.
Column widths
By default, the width of each column is determined by the number of columns and the table’s width. So if you have a table with three columns that spans the standard width of the editor, it’s roughly 750px wide, and the three columns are divided equally.
If you decide to disable the ‘Auto size’ toggle for any column — or you resize by dragging the vertical column resizer — that column gets a fixed width and everything gets recalculated. So now you have one fixed column at, say, 150px, and the remaining two get 750/2=375px each.
This logic was already in place, but the UX of resizing wasn’t great. The resizer might jump around while you moved it, and on release it didn’t always stay put. So I started working on improving the interaction across the board.
Inspiration from a user
By coincidence, while I was working on this, a customer asked for a way to input an exact numeric value for column widths. In their published content, a single page has multiple tables — and the first columns need to be the same size. But without fine-grained control, it was easy to get deviations.
A big part of why we improve the editor is so that users like you can improve your own published docs. And while their suggestion for a dialog with an input field would take users out of the flow of the task at hand, I thought the core idea was great.
A quick exploration lead to a middle-ground solution: I added a tooltip that gives you a live px
number as you drag a column.
With this final implementation, we have the best of both worlds. A single interaction to resize and fine-grained control by communicating the current width. And an added bonus: before you drag, hold down shift
to drag in increments of 5px instead of 1px.
Making the table more WYSIWYG
Previously, the very first cell in each row would have additional space to the left. This space was only there to accommodate the Row options button in our editor, so it would not translate to published content and the first cell would have the same padding as any other cell.
This didn’t sit well with me, and was one of the first things that I saw needed fixing. At GitBook we strive to be as close as possible to our editor being "What-You-See-Is-What-You-Get”. That means content in the editor should reflect the published version as close to 1:1 as possible — and the Row options button wasn’t meeting that goal.

The solution turned out to be more complex than I expected, though. Let’s consider the table block and its layout:

As I explained above, a table spans the full width of the editor and each column auto-sizes, so the entire available editor width is divided over the number of columns you have. A column's min-width is 100px, so if you add or resize columns to be wider, your table should become scrollable due to the table having overflow-x: auto
.
We needed to take the Row options button out of the cell, so my first thought was to position it outside of the table box, and align with the corresponding row.
I ended up trying three different approaches before I found the best solution:
Approach 1: position: absolute
and display outside the row
My first thought was to position the button absolute
and place it relative to its row, but with a negative left
value. But this didn’t work — while the overflow-x: scroll
allows the table content to be scrollable, a grandparent wrapper around the entire table was using overflow: hidden
. So any content outside the table's bounding box was cut off, even when absolute
.
Approach 2: What about position: fixed
?
No luck — it would be positioned relative to the viewport so as you scrolled. They’d fly all over the place!
Approach 3: Move the button out of the table’s overflow-hidden wrappers
My next idea was to build on the approach above, but position the row settings button higher up in the tree outside the overflow-x: hidden
wrapper(s). Unfortunately, this would cause the row settings button to not have a lot of the innate state and context that each row gets initialized with.
That meant lifting up a lot of state or creating whole new models for sharing state across the table — including a lot of the logic we wrote to handle the drag and drop of rows. That was a good enough reason to keep looking for alternatives.
Final approach: The button is a cell… but also not really?
In the end, I went with an approach that combined everything above. The row settings button is lifted outside of the first cell, but sits within the row. So in the DOM within each <tr/> row, next to each <td/> cells that was defined, we also have an extra div at the very start. This div in turn has the button positioned absolute
so that it aligns nicely.
But this obviously creates visual problems: our button is not table data, so it can’t display as a cell! That’s where CSS kicks in. While your table may look like it only has 3 columns, it actually has 4. I just tweaked which table elements gets borders that make the table look table-y, and which don’t.

The side-effects are that the table content gets pushed horizontally by the X amount of pixels that your button wrapper takes up. This was relatively easy to fix by adding a transform: translateX(<buttonWidth>)
to the table’s wrapper.
Upgrading Card blocks
Cards are another super important block in GitBook, with a wide range of uses. Whether you’re adding key details of an important concept, or linking to other parts of your docs — it’s very highly adaptable. But the UX of editing cards was running behind, so I’ve been focused on improving that user experience.
To first provide some context, cards allow different types of data fields:
Text
Number (any non-numeric value is not accepted)
Users
Links
File uploads
Rating (1 to 5)
Checkbox
Select (both multi and single-select labels)
Cover image
The problem was that the distinction between these fields just wasn’t clear at all — especially when it came to empty fields. How did you know whether to add a number, a user or a link to an empty space in a card? Improving the process of adding data to cards became my first priority.
Number fields in cards
Number fields can be quite handy. They have different styling to text fields making them more distinct+ plus, number fields are auto-formatted (so you get 1,000 and not 1000). However, number fields looked just like any other input field — and while the text field did display a placeholder, the number field did not.
I managed to alleviate a lot of confusion and frustration by identifying these issues and adding just two small changes:
When you try to type non-numeric values, a tooltip now tells you this field only accepts numbers.
Empty number fields now say “Enter a number...”.
Placeholders for ‘empty’ fields
For some of data fields I listed above, there is always a default value or UI. For example, the checkbox field shows an untoggled checkbox, and the rating field defaults to 0 stars so you see 5 empty stars.
For the select, users, file and link fields, I added placeholder buttons that allow for quickly inserting a value for the given data type. A similar placeholder makes it obvious and easy to add a cover image, too.
Improving the cards block default state
When you inserted a new cards block, GitBook would create three cards with three empty text fields. But this wasn’t very intuitive! Three cards meant the editor width was filled with the most cards you can currently place on the x axis, and you couldn’t easily tell there were more types of field you could add.
I didn’t want to overload you by adding every field type to a new card — it’s unlikely anyone would need them all, and removing them would be annoying. In the end, I decided that inserting a single card with one empty text field was the best option.
To nudge writers to explore more options, I also added two placeholders — one to add a new card to the block, and one to add fields to your cards.
Our ongoing mission…
Alongside these bigger improvements that I’ve been focusing on, the rest of the editor team are busy making other smaller improvements and fixing bugs across the editor. This is an ongoing effort, and we’ve already shipped a lot of stuff — you can read about them in our recent blog post or head to the changelog to see more.
We’re still working through the backlog, but if you want to report something that isn’t working or could work better, please leave feedback using the Bucket integration within the GitBook app, or join our GitHub community and leave a bug report.
As you’ve read here, we read all this feedback and it’s super valuable, so please keep it coming!
Our goal is to make the GitBook editor the best on the market. It’s a core part of the GitBook experience and we believe no other dedicated docs platform can match it. But we know there’s still more we can do.
The rest of the team will have more to share about editor improvements soon, so stay tuned.
Recently we’ve been putting a lot of focus on honing published documentation to give your users the very best experience when they visit your docs. But while that is at the core of our product, so is the editing experience within the GitBook app. Especially for you folks who use it every day.
We think our editor is already pretty great, but there’s always room for improvement. And we know that if a documentation platform’s editing experience sucks, nobody is going to enjoy using it. Anything that makes it harder to edit your docs is a barrier to keeping your docs up to date, and that’s never a good thing.
That’s why we’re putting in a consistent effort to improve our editor, now and going forward. Over the last few weeks we’ve shipped a ton of stuff — you can check out our recent blog post to read about most of them, or head to the changelog to learn about them all in even more detail.
But I wanted to dive into some of these improvements in some more detail today, as an example of the work that’s been happening behind the scenes.
Building better Tables
Tables are pretty complex elements. They need to support a lot of interactions and data entry options — including reordering columns and rows, setting the column widths, navigating table cells using the keyboard, and much more.
Tables in GitBook were written with react-native web, which means a number of abstractions — especially on the level of event handling. Since the table editor is a very interaction-heavy block, it’s no surprise that there were some issues.
To overcome these issues with reordering, resizing, navigation, selection, and the general UX, I realized the best solution was to completely rewrite the block to use modern React.
Let’s take a closer look at some of the table’s inner workings and how it affected some of the features we’ve worked on.
Column widths
By default, the width of each column is determined by the number of columns and the table’s width. So if you have a table with three columns that spans the standard width of the editor, it’s roughly 750px wide, and the three columns are divided equally.
If you decide to disable the ‘Auto size’ toggle for any column — or you resize by dragging the vertical column resizer — that column gets a fixed width and everything gets recalculated. So now you have one fixed column at, say, 150px, and the remaining two get 750/2=375px each.
This logic was already in place, but the UX of resizing wasn’t great. The resizer might jump around while you moved it, and on release it didn’t always stay put. So I started working on improving the interaction across the board.
Inspiration from a user
By coincidence, while I was working on this, a customer asked for a way to input an exact numeric value for column widths. In their published content, a single page has multiple tables — and the first columns need to be the same size. But without fine-grained control, it was easy to get deviations.
A big part of why we improve the editor is so that users like you can improve your own published docs. And while their suggestion for a dialog with an input field would take users out of the flow of the task at hand, I thought the core idea was great.
A quick exploration lead to a middle-ground solution: I added a tooltip that gives you a live px
number as you drag a column.
With this final implementation, we have the best of both worlds. A single interaction to resize and fine-grained control by communicating the current width. And an added bonus: before you drag, hold down shift
to drag in increments of 5px instead of 1px.
Making the table more WYSIWYG
Previously, the very first cell in each row would have additional space to the left. This space was only there to accommodate the Row options button in our editor, so it would not translate to published content and the first cell would have the same padding as any other cell.
This didn’t sit well with me, and was one of the first things that I saw needed fixing. At GitBook we strive to be as close as possible to our editor being "What-You-See-Is-What-You-Get”. That means content in the editor should reflect the published version as close to 1:1 as possible — and the Row options button wasn’t meeting that goal.

The solution turned out to be more complex than I expected, though. Let’s consider the table block and its layout:

As I explained above, a table spans the full width of the editor and each column auto-sizes, so the entire available editor width is divided over the number of columns you have. A column's min-width is 100px, so if you add or resize columns to be wider, your table should become scrollable due to the table having overflow-x: auto
.
We needed to take the Row options button out of the cell, so my first thought was to position it outside of the table box, and align with the corresponding row.
I ended up trying three different approaches before I found the best solution:
Approach 1: position: absolute
and display outside the row
My first thought was to position the button absolute
and place it relative to its row, but with a negative left
value. But this didn’t work — while the overflow-x: scroll
allows the table content to be scrollable, a grandparent wrapper around the entire table was using overflow: hidden
. So any content outside the table's bounding box was cut off, even when absolute
.
Approach 2: What about position: fixed
?
No luck — it would be positioned relative to the viewport so as you scrolled. They’d fly all over the place!
Approach 3: Move the button out of the table’s overflow-hidden wrappers
My next idea was to build on the approach above, but position the row settings button higher up in the tree outside the overflow-x: hidden
wrapper(s). Unfortunately, this would cause the row settings button to not have a lot of the innate state and context that each row gets initialized with.
That meant lifting up a lot of state or creating whole new models for sharing state across the table — including a lot of the logic we wrote to handle the drag and drop of rows. That was a good enough reason to keep looking for alternatives.
Final approach: The button is a cell… but also not really?
In the end, I went with an approach that combined everything above. The row settings button is lifted outside of the first cell, but sits within the row. So in the DOM within each <tr/> row, next to each <td/> cells that was defined, we also have an extra div at the very start. This div in turn has the button positioned absolute
so that it aligns nicely.
But this obviously creates visual problems: our button is not table data, so it can’t display as a cell! That’s where CSS kicks in. While your table may look like it only has 3 columns, it actually has 4. I just tweaked which table elements gets borders that make the table look table-y, and which don’t.

The side-effects are that the table content gets pushed horizontally by the X amount of pixels that your button wrapper takes up. This was relatively easy to fix by adding a transform: translateX(<buttonWidth>)
to the table’s wrapper.
Upgrading Card blocks
Cards are another super important block in GitBook, with a wide range of uses. Whether you’re adding key details of an important concept, or linking to other parts of your docs — it’s very highly adaptable. But the UX of editing cards was running behind, so I’ve been focused on improving that user experience.
To first provide some context, cards allow different types of data fields:
Text
Number (any non-numeric value is not accepted)
Users
Links
File uploads
Rating (1 to 5)
Checkbox
Select (both multi and single-select labels)
Cover image
The problem was that the distinction between these fields just wasn’t clear at all — especially when it came to empty fields. How did you know whether to add a number, a user or a link to an empty space in a card? Improving the process of adding data to cards became my first priority.
Number fields in cards
Number fields can be quite handy. They have different styling to text fields making them more distinct+ plus, number fields are auto-formatted (so you get 1,000 and not 1000). However, number fields looked just like any other input field — and while the text field did display a placeholder, the number field did not.
I managed to alleviate a lot of confusion and frustration by identifying these issues and adding just two small changes:
When you try to type non-numeric values, a tooltip now tells you this field only accepts numbers.
Empty number fields now say “Enter a number...”.
Placeholders for ‘empty’ fields
For some of data fields I listed above, there is always a default value or UI. For example, the checkbox field shows an untoggled checkbox, and the rating field defaults to 0 stars so you see 5 empty stars.
For the select, users, file and link fields, I added placeholder buttons that allow for quickly inserting a value for the given data type. A similar placeholder makes it obvious and easy to add a cover image, too.
Improving the cards block default state
When you inserted a new cards block, GitBook would create three cards with three empty text fields. But this wasn’t very intuitive! Three cards meant the editor width was filled with the most cards you can currently place on the x axis, and you couldn’t easily tell there were more types of field you could add.
I didn’t want to overload you by adding every field type to a new card — it’s unlikely anyone would need them all, and removing them would be annoying. In the end, I decided that inserting a single card with one empty text field was the best option.
To nudge writers to explore more options, I also added two placeholders — one to add a new card to the block, and one to add fields to your cards.
Our ongoing mission…
Alongside these bigger improvements that I’ve been focusing on, the rest of the editor team are busy making other smaller improvements and fixing bugs across the editor. This is an ongoing effort, and we’ve already shipped a lot of stuff — you can read about them in our recent blog post or head to the changelog to see more.
We’re still working through the backlog, but if you want to report something that isn’t working or could work better, please leave feedback using the Bucket integration within the GitBook app, or join our GitHub community and leave a bug report.
As you’ve read here, we read all this feedback and it’s super valuable, so please keep it coming!
Our goal is to make the GitBook editor the best on the market. It’s a core part of the GitBook experience and we believe no other dedicated docs platform can match it. But we know there’s still more we can do.
The rest of the team will have more to share about editor improvements soon, so stay tuned.
Recently we’ve been putting a lot of focus on honing published documentation to give your users the very best experience when they visit your docs. But while that is at the core of our product, so is the editing experience within the GitBook app. Especially for you folks who use it every day.
We think our editor is already pretty great, but there’s always room for improvement. And we know that if a documentation platform’s editing experience sucks, nobody is going to enjoy using it. Anything that makes it harder to edit your docs is a barrier to keeping your docs up to date, and that’s never a good thing.
That’s why we’re putting in a consistent effort to improve our editor, now and going forward. Over the last few weeks we’ve shipped a ton of stuff — you can check out our recent blog post to read about most of them, or head to the changelog to learn about them all in even more detail.
But I wanted to dive into some of these improvements in some more detail today, as an example of the work that’s been happening behind the scenes.
Building better Tables
Tables are pretty complex elements. They need to support a lot of interactions and data entry options — including reordering columns and rows, setting the column widths, navigating table cells using the keyboard, and much more.
Tables in GitBook were written with react-native web, which means a number of abstractions — especially on the level of event handling. Since the table editor is a very interaction-heavy block, it’s no surprise that there were some issues.
To overcome these issues with reordering, resizing, navigation, selection, and the general UX, I realized the best solution was to completely rewrite the block to use modern React.
Let’s take a closer look at some of the table’s inner workings and how it affected some of the features we’ve worked on.
Column widths
By default, the width of each column is determined by the number of columns and the table’s width. So if you have a table with three columns that spans the standard width of the editor, it’s roughly 750px wide, and the three columns are divided equally.
If you decide to disable the ‘Auto size’ toggle for any column — or you resize by dragging the vertical column resizer — that column gets a fixed width and everything gets recalculated. So now you have one fixed column at, say, 150px, and the remaining two get 750/2=375px each.
This logic was already in place, but the UX of resizing wasn’t great. The resizer might jump around while you moved it, and on release it didn’t always stay put. So I started working on improving the interaction across the board.
Inspiration from a user
By coincidence, while I was working on this, a customer asked for a way to input an exact numeric value for column widths. In their published content, a single page has multiple tables — and the first columns need to be the same size. But without fine-grained control, it was easy to get deviations.
A big part of why we improve the editor is so that users like you can improve your own published docs. And while their suggestion for a dialog with an input field would take users out of the flow of the task at hand, I thought the core idea was great.
A quick exploration lead to a middle-ground solution: I added a tooltip that gives you a live px
number as you drag a column.
With this final implementation, we have the best of both worlds. A single interaction to resize and fine-grained control by communicating the current width. And an added bonus: before you drag, hold down shift
to drag in increments of 5px instead of 1px.
Making the table more WYSIWYG
Previously, the very first cell in each row would have additional space to the left. This space was only there to accommodate the Row options button in our editor, so it would not translate to published content and the first cell would have the same padding as any other cell.
This didn’t sit well with me, and was one of the first things that I saw needed fixing. At GitBook we strive to be as close as possible to our editor being "What-You-See-Is-What-You-Get”. That means content in the editor should reflect the published version as close to 1:1 as possible — and the Row options button wasn’t meeting that goal.

The solution turned out to be more complex than I expected, though. Let’s consider the table block and its layout:

As I explained above, a table spans the full width of the editor and each column auto-sizes, so the entire available editor width is divided over the number of columns you have. A column's min-width is 100px, so if you add or resize columns to be wider, your table should become scrollable due to the table having overflow-x: auto
.
We needed to take the Row options button out of the cell, so my first thought was to position it outside of the table box, and align with the corresponding row.
I ended up trying three different approaches before I found the best solution:
Approach 1: position: absolute
and display outside the row
My first thought was to position the button absolute
and place it relative to its row, but with a negative left
value. But this didn’t work — while the overflow-x: scroll
allows the table content to be scrollable, a grandparent wrapper around the entire table was using overflow: hidden
. So any content outside the table's bounding box was cut off, even when absolute
.
Approach 2: What about position: fixed
?
No luck — it would be positioned relative to the viewport so as you scrolled. They’d fly all over the place!
Approach 3: Move the button out of the table’s overflow-hidden wrappers
My next idea was to build on the approach above, but position the row settings button higher up in the tree outside the overflow-x: hidden
wrapper(s). Unfortunately, this would cause the row settings button to not have a lot of the innate state and context that each row gets initialized with.
That meant lifting up a lot of state or creating whole new models for sharing state across the table — including a lot of the logic we wrote to handle the drag and drop of rows. That was a good enough reason to keep looking for alternatives.
Final approach: The button is a cell… but also not really?
In the end, I went with an approach that combined everything above. The row settings button is lifted outside of the first cell, but sits within the row. So in the DOM within each <tr/> row, next to each <td/> cells that was defined, we also have an extra div at the very start. This div in turn has the button positioned absolute
so that it aligns nicely.
But this obviously creates visual problems: our button is not table data, so it can’t display as a cell! That’s where CSS kicks in. While your table may look like it only has 3 columns, it actually has 4. I just tweaked which table elements gets borders that make the table look table-y, and which don’t.

The side-effects are that the table content gets pushed horizontally by the X amount of pixels that your button wrapper takes up. This was relatively easy to fix by adding a transform: translateX(<buttonWidth>)
to the table’s wrapper.
Upgrading Card blocks
Cards are another super important block in GitBook, with a wide range of uses. Whether you’re adding key details of an important concept, or linking to other parts of your docs — it’s very highly adaptable. But the UX of editing cards was running behind, so I’ve been focused on improving that user experience.
To first provide some context, cards allow different types of data fields:
Text
Number (any non-numeric value is not accepted)
Users
Links
File uploads
Rating (1 to 5)
Checkbox
Select (both multi and single-select labels)
Cover image
The problem was that the distinction between these fields just wasn’t clear at all — especially when it came to empty fields. How did you know whether to add a number, a user or a link to an empty space in a card? Improving the process of adding data to cards became my first priority.
Number fields in cards
Number fields can be quite handy. They have different styling to text fields making them more distinct+ plus, number fields are auto-formatted (so you get 1,000 and not 1000). However, number fields looked just like any other input field — and while the text field did display a placeholder, the number field did not.
I managed to alleviate a lot of confusion and frustration by identifying these issues and adding just two small changes:
When you try to type non-numeric values, a tooltip now tells you this field only accepts numbers.
Empty number fields now say “Enter a number...”.
Placeholders for ‘empty’ fields
For some of data fields I listed above, there is always a default value or UI. For example, the checkbox field shows an untoggled checkbox, and the rating field defaults to 0 stars so you see 5 empty stars.
For the select, users, file and link fields, I added placeholder buttons that allow for quickly inserting a value for the given data type. A similar placeholder makes it obvious and easy to add a cover image, too.
Improving the cards block default state
When you inserted a new cards block, GitBook would create three cards with three empty text fields. But this wasn’t very intuitive! Three cards meant the editor width was filled with the most cards you can currently place on the x axis, and you couldn’t easily tell there were more types of field you could add.
I didn’t want to overload you by adding every field type to a new card — it’s unlikely anyone would need them all, and removing them would be annoying. In the end, I decided that inserting a single card with one empty text field was the best option.
To nudge writers to explore more options, I also added two placeholders — one to add a new card to the block, and one to add fields to your cards.
Our ongoing mission…
Alongside these bigger improvements that I’ve been focusing on, the rest of the editor team are busy making other smaller improvements and fixing bugs across the editor. This is an ongoing effort, and we’ve already shipped a lot of stuff — you can read about them in our recent blog post or head to the changelog to see more.
We’re still working through the backlog, but if you want to report something that isn’t working or could work better, please leave feedback using the Bucket integration within the GitBook app, or join our GitHub community and leave a bug report.
As you’ve read here, we read all this feedback and it’s super valuable, so please keep it coming!
Our goal is to make the GitBook editor the best on the market. It’s a core part of the GitBook experience and we believe no other dedicated docs platform can match it. But we know there’s still more we can do.
The rest of the team will have more to share about editor improvements soon, so stay tuned.
Get the GitBook newsletter
Get the latest product news, useful resources and more in your inbox. 130k+ people read it every month.
Similar posts
Get started for free
Play around with GitBook and set up your docs for free. Add your team and pay when you’re ready.
Get started for free
Play around with GitBook and set up your docs for free. Add your team and pay when you’re ready.
Get started for free
Play around with GitBook and set up your docs for free. Add your team and pay when you’re ready.
Documentation
Documentation
Documentation