Writing a textbook with bookdown
I am working on an outline for a textbook for a fullstack programming course. I want to break my outline into lessons and generate a nice HTML view from the markdown files that will become the basis for an interactive textbook. Bookdown is the tool, documentation can be found here. Here is how I set it up and got started.
Installing R with VS Code
Bookdown using the R programming langauge and markdown documents. So the first step is getting R running with the necessary packages. And I want it integrated in VSCode.
- Install R: https://cloud.r-project.org/ (make sure to check “Save version number in registry”)
- Install VSCode R editor support: https://marketplace.visualstudio.com/items?itemName=REditorSupport.r
- In R install
languageserver
: https://github.com/REditorSupport/languageserver- open RGui on Windows
- run
install.packages("languageserver")
- also install
rmarkdown
with:install.packages("rmarkdown")
- Make sure R is on the system PATH, for me that was
C:\Program Files\R\R-4.2.2\bin
- Close and re-open VS Code if you cannot access R interactive shell from the command-line. There should also be a new terminal option “R Terminal” that opens an interactive shell.
- can double check the packages installed with
install.packages("languageserver")
at the R Terminal, this shouldn’t do anything.
- can double check the packages installed with
Once that was setup, I made a test R script test.R
:
# My first program in R Programming
myString <- "Hello, World!"
print (myString)
And ran R test.R
at the terminal. It worked. So R is up and running!
First Steps with Bookdown
To install bookdown for R, I just need to run (at the R terminal):
install.packages("bookdown")
Minimal Example
Bookdown is built on rmarkdown
. A demo for staring a project can be found here. However, I used the documentation minimal example here.
render_book()
I downloaded index.Rmd
, then in the R terminal I ran:
bookdown::render_book('index.Rmd', 'all')
This generated a folder _book/
with a static HTML version of the content. This is the built book, and all arguments can be found in the documentation:
bookdown::render_book(input = ".", output_format = NULL, ..., clean = TRUE,
envir = parent.frame(), clean_envir = !interactive(),
output_dir = NULL, new_session = NA, preview = FALSE,
config_file = "_bookdown.yml")
By leaving output_format
empty, the book is rendered as the first output format specified in the YAML metadata of the first .Rmd
file or a separate YAML file _output.yml
(see here). When you set preview = TRUE
, only the Rmd files specified in the input
argument are rendered, which can be convenient when previewing a certain chapter, since you do not recompile the whole book, but when publishing a book, this argument should certainly be set to FALSE
.
clean_book()
You can delete the built book with:
bookdown::clean_book(TRUE)
But be careful that things are version controlled with git so you don’t lose anything.
Viewing the Book
To view the static site in the _book/
directory. I installed the VSCode extension Live Preview. All I need to do is select one of the .html
files, click the preview button in the code editor, and there it is. I can also just navigate to http://127.0.0.1:3000/_book/
in my browser. It even updates as I add chapters and redo the render_book()
command.
If the book is getting long and the pandoc conversion step of render_book()
is taking awhile, then an individual chapter can be re-rendered using:
bookdown::preview_chapter("index.md")
Practical Example
Chapter Order
My default, bookdown will render files in the order of their name (e.g., 01-something.Rmd
, 02-something-else.Rmd
, etc.). I don’t want to provide an order in filenames, because I may want to remix and rematch. So I just need to override this behavior by making a _bookdown.yml
file in the book directory and providing a file list:
rmd_files: ["02.Rmd", "01.Rmd", "index.Rmd"]
Filenames that start with an _
underscore are skipped. The file index.Rmd
will always be treated as the first even if re-ordered in the list.
I can create an appendix with # (APPENDIX) Appendix {-}
at the top of a file, the {-}
characters tell markdown not to give this a section number
Front Matter
For now, I want the book to render as a gitbook, which provides nice links and a navigation pane. So the front-matter of the index file should look like so:
# index.Rmd
---
title: "A Book"
author: "Ben"
site: bookdown::bookdown_site
documentclass: book
output:
bookdown::gitbook: default
#bookdown::pdf_book: default
---
The site: bookdown::bookdown_site
pair calls bookdown::render_book()
, and the output is specified as bookdown::gitbook
. The outputs can also be specified in a separate _output.yml
document as shown here, which is maybe better from an organizational standpoint. So now if I just run:
bookdown::render_book()
then everything works. Also because I have the _bookdown.yml
in my root directory, which render_book()
looks for by default, my chapter order and other variables are set correctly.
Markdown
But what if I don’t care about .Rmd
files, because I don’t plan to have integrated R code? Well, I can just use .md
markdown files! If I changed all the filenames and replace the _bookdown.yml
file list with:
# _bookdown.yml
rmd_files: ["02.md", "01.md", "index.md"]
And when I bookdown::render_book()
and visit http://127.0.0.1:3000/_book/
, then everything looks good!
For the #
heading in each file, if I add {-}
, then it will skip section numbering.
Publishing on GitHub
I want to create github repo for the project, and I would also like to view the rendered site as updates are made. There are instructions here.
Other useful stuff
- you can add bibliographies and citations: https://bookdown.org/yihui/bookdown/citations.html
- you can add HTML widgets: https://bookdown.org/yihui/bookdown/html-widgets.html
- the
_bookdown.yml
file is highly customizable: https://bookdown.org/yihui/bookdown/configuration.html#configuration