Introduction to JSX
JSX is an extension of javascript that's used by React as well as several other single page application frameworks. TSX is almost identical, except TSX adds the ability to more strictly define types. For the purposes of using ULLD, TSX and JSX can be referenced interchangeably.
The primary goal of JSX is to add the ability to write multiple different languages in a single file. React components, which are written in TSX1, work primarily as a render function. React components are functions that either return some sort of valid HTML, or null
if the internal logic determines that nothing visible should be returned.
Through the great work at the people over at MDX, we can now embed entire React components directly in markdown files, with the .mdx
file extension. All of the original functionality of markdown remains intact, and in ULLD is actually extended quite a bit, but users can now include neatly bundled HTML, CSS, and Javascript in pre-defined React components.
All you need to know is how to properly pass in the data that these components need to calculate what they should return. If you're already familiar with JSX, you can move on to the components documentation. Otherwise, a quick review of this documentation is important if you want to get the most from ULLD. Don't worry; It's incredibly simple.
How to read documentation
Regardless of whether or not a component is actually built using Typescript (TSX), a similar syntax is used to define the properties that any given component accepts. Because these types are often defined in the same file as regular javascript code, they use an intentionally similar syntax, but not an identical syntax to regular javascript.
React Component Syntax
JSX intentionally mirrors the appearance of HTML, and functions in a very similar manner. React components will always start with a capital letter, where as regular HTML elements can also be included in your note, with all HTML elements starting with a lower case letter.
Children Property
All properties are passed into the component using the syntax below, but children are passed in using a unique syntax.
Self Closing Components
If a component does not accept a children
property, it can be 'self closed' using this syntax:
Components that do not accept children
can also be closed using this syntax:
While unnecessary, this follows an identical syntax to components that do accept children. Notice that when a component is self closing, there is a /
just before the closing >
, and only one instance of that component for each time it is rendered. When a component is not self closing, the /
follows the <
in the second tag for each element rendered.
In the example above, the property name
follows a similar syntax to other component properties, and it is possible to pass in children
in the same manner, but children
adds the additional ability to pass the value in as:
Other Component Properties
Passing in other properties to react components follows a similar syntax to that of the name
property above, but there are slight variations for different types. All properties can be wrapped in a pair of curly braces as:
Strings
The use of curly braces is important for other value types, but is optional in most cases for strings. Note that the name
property in the example above has a value that is still wrapped in quotations along with the curly braces. There is no difference between single and double quotes, and backticks `
can also be used.2
If your string property includes a single quotation, wrap it in double quotes. If it has a double quote, wrap it in single quotes. Alternatively, you can escape special characters, including quotes in a string with a \
before the special character. This is more or less universal across programming languages, and applies to all special characters. Adding a leading \
removes the "special" meaning of that character, and makes it function as the literal character.
String Literals
Note: String literals must be wrapped in curly braces when passed in as a component property. Curly braces are optional with both single and double quotes.
While single quotes and double quotes are interchangable apart from the ability to wrap that same character as described above, backticks add additional functionality. Where as Python has f-strings
with the following syntax:
Javascript uses backticks to accomplish the same thing:
Note that phi
inside of the `...${phi}` {:tsx}
block is just regular javascript. Code inside of the `${...}` {:tsx}
is no longer part of a string. It is regular javascript code that is then inserted into that part of the string. This means that you can do calculations directly in that part of the string, or use variables as I've done in the example above. For example, we can write:
Note that all latex math in the example above is wrapped in single $
symbols, since the math should appear inline, and the leading \
that is part of Latex syntax is escaped with another \
. Just like a \
can escape other special characters, it can also escape itself. In this case, we are escaping the \
with another \
to tell Javascript that the first \
is not intended to escape another character and should be treated as a literal \
.
Children String Properties
MDX and other markdown to html parsers wrap new lines, including the children property in a paragraph element. This can cause issues in cases where the children
component is specifically of a type string. ULLD attempts to parse these paragraphs dynamically, but in cases of a parsing error where a property of type string
is critical like in a JupyterCell component, you can wrap the children
in a pair of curly braces with a pair of backticks inside.
Backticks allow almost completely plain text writing style, where as certain content like line breaks will throw errors inside of either single or double quoted strings. The end result would look like:
Numbers
As is the case in most programming languages, "3.14"{:tsx}
is not the same as 3.14{:tsx}
. If a property requires a type of number
, there is a significant chance that there will be some sort of mathematical operations taking place within that component. Ironically, strings also accept several mathematical functions, but they will return a significantly different value as demonstrated below:
To pass a number into a React component, simply wrap the value in curly braces without any quotations.
Keep in mind that Javascript has a global Math{:tsx}
object. You can see documentation for that object here. Using that object, we can rewrite the example above as:
Many number and string properties are in the process of being converted to a format where they will also accept a function. That function will be provided a greatly extended Math object as an argument, and will be able to be used to generate calculations directly in your markdown files, without requiring an entire Jupyter cell.
Follow this blog post to stay up to day on features coming to ULLD
Boolean Props
If a value is passed in as just the key of that value without any actual value declared, it is interpreted as true
. Therefore, if a component expects a value of applyModal
to be of a type boolean
, and you want that value to be true
, <ThatComponent applyModal={true} />{:tsx}
and <ThatComponent applyModal />{:tsx}
are equivalent.
Boolean properties are passed in using an almost identical syntax to number properties, and like number properties, string
properties are not equivalent... kind of. Javascript along with many other programming languages has a concept of 'truthy' and 'falsy'. In Javascript, all strings will evaluate to true if they are tested using an if
statement and many other comparison operators, but will be false
if tested using the comparision "true" === true{:tsx}
.
Since MDX files are not evaluated for type correctness, it can be very easy for new users to mistakenly pass in a value as a string and think they have the right value, if they expect that value to be true, only to find out that it will never be evaluated as false. The truthyness of strings gives a scenario in which the following occurs.
While the example above is intentionally over simplified and almost entirely useless, we can demonstrate the truthy and falsy nature of Javascript. If we include this component with the following syntax:
Output
4 will return 4, while 2 will return 2
ReactNode Type
React provides dozens of it's own types, but one that appears quite often is that of ReactNode
. The ReactNode
type is pretty much anything that React can render as is. It accepts a boolean, a string, a number, or another rendered component.
Extended Types
"Extending" types is almost universal among programming languages as well. If type A
extends type B
, then type A
will have all of the same properties as B
, and any extra properties that are unique to A
. If A
and B
have a property in common, the property of A
will overwrite that of B
.
ULLD takes advantage of this structure throughout it's MDX interface, but two instances are currently the most common.
Extending ColorProps
Many components like the Ul
, Color
and Hl
components accept props that extend the ColorProps
type. You can review that documentation here. This is a single object that has a set of default boolean values. Those default values can be overridden in your configuration file, where new ones can be added as well. Despite the fact that those properties are not explicitly described in the documentation of those components, the Extends: ... implies that all properties of the extended type are also accepted.
Extending HTML Attributes
For those familiar with HTML or CSS, most components extend the HTML attributes of the outer most element. This wasn't documented intentionally to avoid complicating the documentation for those without developer experience. However, be aware that while ULLD relies on Tailwind for it's theme-ability, .mdx
files are currently not evaluated for their className
content.
If you want to add you own .css
or .scss
files, take a look at the documentation here for how to provide a variety of your own files to the build process.
If you notice a bug with the way HTML attributes are handled, please submit an issue on Github here.
Footnotes
-
JSX is still used on occasion, but is considered exceedingly bad practice if an alternative type safety approach isn't taken. ↩
-
Be careful not to confuse backticks and single quotes for string literals. True story: I spent 9 hours in a coffee shop when I was first learning to code, trying to figure out that a single quote,
'
, is not the same as a backtick,`
. Don't be me. ↩