Code Modules — Docs

BBEdit and Its Include System

BBEdit and Its Include System


BBEdit, made by Bare Bones, is a venerable and well-loved text editor, particularly suited for creating hand coded websites. It’s not free. I’m using BBEdit v14.0.1, as of this writing, but BBEdit v12.1.6 or later will work. Prior to v12.1.6, the include limit was 16 levels deep — one file calling the next. Because I begged, Bare Bones increased it to 32 levels deep, which is plenty for the needs of Code Modules. In a pinch, BBEdit versions prior to v12.1.6 will work except for the most deeply nested modules.


BBEdit variable names need to begin and end with an octothorpe (#). They cannot have spaces. They may have underscores or hyphens. BBEdit’s unforced convention is that they be all caps. An example: #MODULE#. But these variables are not case-sensitive: #Module# or #module# or #MODULE# are interchangeable. I use an all-caps kebab case convention, but it’s not required.

Variables are passed down the chain, from parent to child, even if not explicitly set in each intermediate file. This permits all sorts of things. One is that variables can be defined high-up in the chain of includes and then redefined — overwritten — lower in the chain.

When used, the variable will be replaced by its value. If the include is <a href="#URL#">#LINK-TEXT#</a>, then what is returned might be: <a href="">Pipsqueak Productions</a>.

String variables should be quoted using either single or double quotes. The quotes will not be part of the value of the variable. If the string contains a single quote, use double quotes to delimit the string, and vice versa. Even within quotes of either type, a string can contain a variable. If:
#PAGE# = "Home",
#COMPANY# = "CM Company", and
#TITLE# = '#PAGE# — #COMPANY#', then
#TITLE# is Home — CM Company.

One can assign one variable to another, e.g. #INDENT# = #WIDE-INDENT# where #WIDE-INDENT# was defined somewhere in the parent calling chain. However, the variable being used for the assignment (#WIDE-INDENT#) cannot be defined in the same include. Sometimes, it would be handy if you could assign a value to #FIRST-NAME# and then, in the same include, use #FIRST-NAME# as part of the value for #FULL-NAME#, but you can’t.

BBEdit provides some pre-defined variables which they refer to as “placeholders”. I find the following to be particularly useful:

  • #DATETIME XXX# (where XXX is an ICU format string like "YYYY-MM-dd"),
  • #LIPSUM [OPTIONS]# (see User Guide for options syntax), and
  • #ROOT#

Each are described in BBEdit’s online User Guide, Appendix C.

Code Modules reserves a few standard variable names as a convention:

  • #INDENT#
  • #ITEM#
  • #LIST#
  • #MODULE#
  • #PAGE#
  • #TYPE#


Internal module files use a shtml extension. My convention for all module filenames is to use an all lowercase kebab style — all lowercase with spaces replaced by hyphens. If there is only one choice — of a list, for example — the filename should be default.shtml. Otherwise, name it to something related to its purpose.

No Conditionals

One limitation of BBEdit’s include feature is that there are no conditionals at all. It would be handy to branch based upon the value of a variable, but, other than shelling out to another language like Applescript or Bash, I haven’t found a way to do that.

One can, however, use a variable value as part of the file path that is used to call child files. For example, one can choose which template a module should use by setting a template variable in the calling include and then using the #TEMPLATE# variable as part of a calling path inside the module.

This is a key to making Code Modules work and minimizes the inconvenience of not being able to use conditionals.

Template Preferences in BBEdit

Each website should have a unique modules folder. For each of my sites, at the root level of my website’s file organization, I have app/ and public/ folders. Modules are kept inside the app/modules/ directory.

BBEdit has a preference that must be set for each website to tell it where to look for templates and includes. I specify the app/modules/ directory as the “Search for Includes” location in BBEdit’s preferences for that website.

The app/ folder is kept local and is not pushed to the website’s production server. The app/ folder should be part of the website’s Git repo.

BBEdit Include Syntax

Persistent Includes

BBEdit has two variations for its include syntax. The more complex version — a “persistent include” — allows for variables to be defined and passed to the child file. The simpler version just includes the child file without redefining variables. In both versions, variables that have been defined higher in the chain are passed along whether explicitly passed or not.

In BBEdit, pages are updated using the menu command [keyboard shortcut]

Markup ⟶ Update ⟶ Document [∧⌘U]
or, for updating all the pages of a website at one time,
Markup ⟶ Update ⟶ Site.
When a page is updated, the code for a persistent include will remain in a top level calling page.

In a top level calling page, what is returned by the include will be inserted between the two HTML comments which make up a persistent include’s syntax. Because its two parts are wrapped as HTML comments, the code of the include itself won’t be visible to the user viewing a top level HTML page, although it will be visible like other HTML comments in that page’s source code.

In an intermediate file (i.e. not a top level calling file), a persistent include, like a simple include, just passes along content returned from the child to the calling parent without being replaced.

Let’s break down BBEdit’s persistent include syntax, because we’re going to be seeing it a lot.

There are two parts, each wrapped as an HTML comment.

Persistent Include Generalized
1<!-- #bbinclude "[path-to-call-child-include-file]"
3#[VARIABLE-1]# = '[value]'
4#[VARIABLE-2]# = '[value]'
6<!-- end bbinclude -->

Here’s a specific example:

1<!-- #bbinclude "../types/default.shtml"
3#URL# = ""
4#CLASS# = "icon-twitter"
6<!-- end bbinclude -->

The returned result will be inserted between lines 5 and 6, the two parts each wrapped in HTML comment syntax. Anything already between those two parts will be replaced.

Let’s walk through these lines.

Line 1

<!--#bbinclude "[path-to-call-child-include-file]"
includes the calling path to the child file being called. That file, in turn, can call other files. The calling path used can be relative or absolute. The root has been set in BBEdit’s settings and, in my system, begins at [website main folder]/app/modules/. When it’s relative, it’s relative to itself, not to the calling file.

Line 2

is just boilerplate. All it does is stop BBEdit from adding an empty return. Frankly, BBEdit should provide an option to make not adding an empty return the default behavior. Because I don’t like blank lines in my source code, I need to put the #bbincludeoptions line into every persistent include I write.

Lines 3 and 4 are variables being defined; these lines assign values to variables. The quotes are needed for string values and the variables won’t include the quotes.

Last Line

<!--end bbinclude-->
The last line is always the same in every persistent include. BBEdit needs it to delimit the end of the call. Everything returned from the call will be inserted just before this end line.

I reserve the #COMMENT# variable for comment statements to internally document something in a persistent include. I often have multiple #COMMENT# variable values in a single include. Subsequent uses overwrite the previous value, but since I never use the value of the #COMMENT# variable elsewhere, I don’t care.

Simple Includes

BBEdit has another form of include — a simple include — that is useful when you don’t need to set the values of any variables. It looks like this:

Simple Include Generalized
1#bbinclude "[path-to-call-child-include-file]"

A simple include is not wrapped in an HTML comment. It just contains the #bbinclude statement followed by a path to the child file. Either single or double quotes are required around the path should it contain a space; otherwise quotes are optional. I always quote paths. Any variables defined higher in the calling chain will be passed through a simple include unchanged.

In a top level calling page, what is returned by the child will completely replace a simple include. Because it is replaced, it is generally not used in a top level calling file. But, in a child file in the calling chain, both persistent and simple includes just pass along content returned from their children to the calling parent without being replaced themselves.