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.

PropertyDescription
optionalProperty?An optional property is denoted by a question mark suffix
TypeOne | TypeTwoWhen multiple types are valid for a given property, this list of valid types is separated by a | symbol

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:

<SomeComponent name="Some name property" />

Components that do not accept children can also be closed using this syntax:

<SomeComponent name="Some name property">
</SomeComponent>

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:

<SomeComponent name="Some name property">
    This is the children property here. This component will now receive two properties, 
    the "name" property, and this "children" property. This is often much more
    readable and easy to change.
</SomeComponent>

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:

<SomeComponent name={"Some name property"} />

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

Hint:

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.

const validString1 = "This is a 'valid' string." 
const validString2 = 'This is a "valid" string.'
const validString3 = "And this is a \"valid\" string too!"
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:

phi.py
phi = 1.618
value = f"Phi is {phi}"

Javascript uses backticks to accomplish the same thing:

phi.jsx
const phi = 1.618
const value = `Phi is ${phi}`

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:

<SomeComponent description={`$\\Phi$ is unique in the fact that for $\\Phi=${phi}$, $\\Phi + 1 = ${phi**2}$.`}/>
Hint:

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:

<SomeComponent>
{`This content is now specifically of type string, no matter how many line breaks are added.
In most cases this won't matter, but if you receive an error related to the type of the children 
property on a component that requests specifically a type of string, try this syntax.`}
</SomeComponent>

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:

const piNumber = 3.14
const piString = "3.14"
 
let addedNumber = piNumber + 1 // 4.14
let addedString = piString + 1 // "3.141"

To pass a number into a React component, simply wrap the value in curly braces without any quotations.

<SomeComponent value={3.14} />
Hint:

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:

<SomeComponent 
    value={Math.PI} // 3.141592653589793
/>
Note:

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

Hint:

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.

const ToSquareOrNotToSquare = ({valueIsSquared, value}: {valueIsSquared: boolean, value: number}) => {
    // This is what a component looks like internally.
    // This is where logic would be applied to return the proper value.
   let renderThis = valueIsSquared ? value**2 : value
   return renderThis
}

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:

<ToSquareOrNotToSquare valueIsSquared={"false"} value={2} /> value will return 4,
while <ToSquareOrNotToSquare valueIsSquared={false} value={2} /> will return 2.
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.

<SomeComponent 
    someNumberProperty={Math.PI}
    someStringProperty={`Love ya like a fat kid loves ${Math.PI}`}
    someBooleanProperty={true}
    someReactElementProp={<SomeOtherComponent name="Some property of SomeOtherComponent." />}
    // Do NOT do this:
    // This is actually of type Component.
    // It is valid typescript, but not part of the ReactNode type. While externally developed plugins are free to build for their own use case, internally developed components will never use this type in user accessible components.
    someIncorrectElementProp={SomeOtherComponent} 
/>

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.

Hint:

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

  1. JSX is still used on occasion, but is considered exceedingly bad practice if an alternative type safety approach isn't taken.

  2. 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.

On this page