This New Syntax Wants To Replace JSX

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00First we had JSX, then we had TSX, but we've been stuck with these for years now.
00:00:04Can't they be improved? Well, maybe by TSRX.
00:00:08It's kind of the same, but it's different.
00:00:10We don't have a function, we have a component, we have strings for our text,
00:00:14there's a normal if statement in here, and there's also no return statement.
00:00:17So what is this, why, and should you use it? Let's find out.
00:00:21[Music]
00:00:26Now there might be a few of you that have actually seen code like this before,
00:00:29and that's because it's actually brought to you by the creator of Ripple.
00:00:31This is a new front-end framework that Rich had covered on this channel six months ago,
00:00:35so subscribe to keep up to date with these things.
00:00:38What they've done is they've extracted the syntax used in Ripple,
00:00:41and they made it work with React, Preact, Solid, Vue, and Ripple of course,
00:00:45and a lot of people were pretty hyped for this.
00:00:47Now TSRX describes itself as a way to write UI components that stay readable and co-located,
00:00:52so structure, styling, and control flow live together,
00:00:55and the result stays fully backward compatible with TypeScript.
00:00:58But unless you've used Ripple before, you're probably still a little confused by what that means,
00:01:01so let's go through its features.
00:01:03To start you use this with TSRX files, which does mean that we need a compiler step,
00:01:07but that is super easy to set up with a VIT plugin,
00:01:10and there's also other options for other frameworks and runtimes as well.
00:01:13As for the actual components, instead of writing function here, we write component,
00:01:17and this is mostly just a keyword for the compiler itself,
00:01:20but it also makes it clear that this is going to contain some rendering logic.
00:01:24I'd maybe consider it a small quality of life improvement.
00:01:27One thing to notice though is that we have no return statement here,
00:01:30and that's because TSRX uses statement-based JSX,
00:01:33so you don't need to return a component tree,
00:01:35you just write your markup wherever you want it to render.
00:01:37This means that we can actually drop in another paragraph tag above this card at the top of the component,
00:01:42and it's going to render in where it's written.
00:01:44You can still use a return in a component, but it has to be a bare one,
00:01:47and it's only used for early returning, so UI and logic after this is skipped.
00:01:51It's helped me to think of TSRX components as very linear,
00:01:54so the order that we write the source in is the order of rendering,
00:01:57simply reading from top to bottom,
00:01:59but I can also see that this might make it harder to quickly see what a component is rendering,
00:02:03whereas in something like React, we would just jump straight for the return.
00:02:06Another benefit of statement-based JSX is that we can use a lot more normal JavaScript.
00:02:10For example, conditional rendering is super simple.
00:02:13It is just an if statement with an else-if and else-to if you need them.
00:02:17In the conditions, we simply just need to place our JSX as a statement.
00:02:20This same shape in React often turns into nested ternaries,
00:02:23because in JSX, every branch has to be an expression,
00:02:26so I find that the TSRX version can sometimes read easier,
00:02:29especially when we have a more complex statement,
00:02:31but in the same way, I can also see that this may add more verbosity,
00:02:35especially when you just need a simple condition.
00:02:37It's the same story for switch statements as well.
00:02:39You can just use a normal JavaScript switch with your cases
00:02:41and the JSX you want to render for each one.
00:02:44This is a little more simple than how you would handle this in React,
00:02:47where you need a function to use the same pattern,
00:02:49so TSRX is a little cleaner here,
00:02:51but an area that I personally like TSRX less is in list rendering.
00:02:55Here we ditch .map and instead we use a for-off loop,
00:02:58and TSRX has actually extended this loop so we can get the index out
00:03:01as well as a stable identity with the key.
00:03:03Then when you want to skip an item, you can simply use continue,
00:03:06so again, it's a bit closer to the vanilla JavaScript flow,
00:03:08but as I said, for me, I've gotten very used to using .map, filter, and so on,
00:03:12so I'll probably be sticking to that,
00:03:14and it's also worth noting that you can't use any other loop types
00:03:17like for, for-in, while, and do-while.
00:03:19It is only for-off that this works for.
00:03:21Now continuing the trend of using normal JavaScript,
00:03:23the way that we do error boundaries in TSRX is with a simple try-catch block.
00:03:27Nothing fancy and pretty self-explanatory.
00:03:30We can then also use the same try-catch block if we need async boundaries,
00:03:33we simply just need to add in a pending block
00:03:35and then write your loading component in there.
00:03:38The compiler actually handles taking this code
00:03:40and resolving it to whatever framework you're using,
00:03:42so in React, Preact, and Solid, this actually uses lazy,
00:03:45and in Ripple, it's the Ripple equivalent.
00:03:47Talking of React specifically though,
00:03:48the features that we've looked at so far
00:03:50appear to allow us to break one of React's key rules,
00:03:53the rule of hooks.
00:03:54We can now place them after conditions and early returns,
00:03:57and even inside of loops.
00:03:58They will all work as normal.
00:04:00This lets us better co-locate our code to where it's actually needed,
00:04:03and the final output doesn't even break the rules.
00:04:06The compiler just quietly hoists every hook to the top of the generated function,
00:04:09so React still sees them in a stable order,
00:04:11but you get to write them where they actually belong.
00:04:14Now for me, as someone who's used React for years,
00:04:16this is one of the ones that I struggle to get used to,
00:04:18and it's also a feature where we're making the compiler
00:04:20do a lot more behind-the-scenes magic,
00:04:22specifically around a framework,
00:04:24and I think if I was debugging this,
00:04:26I might get a bit lost as to what code is where.
00:04:28Next up though, we have lexical scoping,
00:04:30so every nested element is creating its own scope,
00:04:32so we're able to declare a const label in three different div blocks here,
00:04:36and they don't collide.
00:04:37There's even one at the top of the function here that nothing is reading,
00:04:40and again, it is not conflicting.
00:04:41The same thing goes for every if, for, switch, or try statement.
00:04:44Each one has its own scopes,
00:04:46so the variables we declare, the functions we run,
00:04:48and the values we derive, they don't leak into the other scopes.
00:04:51This is another one of those features that's focused on co-locating our code,
00:04:54and again, it makes our components read in a top-to-bottom, linear way.
00:04:57Switching things up now from JavaScript, let's talk about styling.
00:05:00In TSRX, we actually have scoped styles,
00:05:02so you can just place a style block in your component,
00:05:04and the CSS that we write in there is scoped only to that component,
00:05:08with a unique hash being attached to the class name when it's compiled.
00:05:11So this card component has a card class,
00:05:13and notice here, it's also trying to use that card class,
00:05:16but it gets none of the card styling,
00:05:17because it has no style block of its own.
00:05:19It doesn't get the style from its parent,
00:05:21because it doesn't have that unique hash.
00:05:22If you do want to share styles across components though,
00:05:24TSRX has a style keyword,
00:05:26so the parent passes the style name in with this keyword
00:05:29to a component that accepts class name as a prop,
00:05:31and it will make sure that that unique hash that it generates goes along with it.
00:05:35So now notice has the same style as parent.
00:05:37You can also technically use a global selector around your styles
00:05:40to escape the scoping and apply those styles globally,
00:05:42but I think that's going to get a bit messy,
00:05:44and you'll lose track of where your styles are coming from.
00:05:46Personally, I'm a tailwind guy through and through,
00:05:48so I probably won't use this feature much,
00:05:50and I'll stick to tailwind,
00:05:51but it's pretty cool nonetheless.
00:05:53Next up, a feature for those of you that have been paying attention.
00:05:56In the code blocks that I've showed,
00:05:57there's been a small difference in the way that text is handled in these statements.
00:06:01Bare text inside of an element has to be double-quoted.
00:06:04We can't just write it in like we can in JSX.
00:06:07You can still use dynamic values though,
00:06:08and in a line like this one,
00:06:10which is in between two double-quoted strings,
00:06:12and TSRX will just simply concatenate this into one string when it compiles.
00:06:16Another option that you have is to simply stick to using a template literal.
00:06:19It gets the same result.
00:06:20For me, this has been one of the paper cuts of using TSRX
00:06:23because I just have so much muscle memory of not using quotes for text.
00:06:26Another text-based feature though,
00:06:27is that TSRX can actually handle strings that contain actual HTML markup,
00:06:31and you have two ways to render this.
00:06:33The first one is by simply using the text keyword,
00:06:35which is going to render in the escaped text,
00:06:38so you can see the literal HTML string,
00:06:40and it's also safe from cross-site scripting.
00:06:42So this is useful when you want to guarantee that something is going to be a string,
00:06:45and the source of that string is a little bit ambiguous,
00:06:48so you don't necessarily know its type when you're writing this code.
00:06:51The second option is for if you want to render the string as HTML,
00:06:54we can simply use the HTML keyword,
00:06:56and this parses it as real HTML,
00:06:58but this feature only works in Ripple with Vue supporting a narrower subset of it.
00:07:02Another feature not related to React,
00:07:03but it might be interesting to those of you that are using Ripple,
00:07:06Vue or Solid is lazy destructuring.
00:07:08If you destruct your props normally in these frameworks,
00:07:10you snapshot each value at call time,
00:07:12and that breaks the per-access reactivity.
00:07:14So in TSRX you can simply add an ampersand before the props,
00:07:18and while it looks like destructuring,
00:07:20each binding is actually compiled back to a property lookup where it's used.
00:07:23So the runtime sees each access individually,
00:07:25so signal updates still trigger re-renders,
00:07:28so it means that you get to keep the destructuring ergonomics,
00:07:30and the framework keeps its reactivity.
00:07:32The final feature I'll show is a nice and simple quality of life one.
00:07:35Have you ever had a prop where the value that you pass to the prop has the same name as that prop,
00:07:40most commonly in something like an on-change function?
00:07:42Well, with TSRX you can actually just write this as shorthand,
00:07:45similar to how we can with JavaScript objects.
00:07:47It's clean and simple.
00:07:49So overall, TSRX feels like it's attempting to blend normal JavaScript flow back into JSX
00:07:53and also add in a few quality of life improvements,
00:07:55and I like quite a lot of its elements.
00:07:57I genuinely think my main downfall with it is that it's too niche and too late
00:08:01when we have AI writing most of our code,
00:08:03and the code that AI is good at writing seems to be JSX and React.
00:08:07With that being said though, I did throw some demos at Claude using TSRX,
00:08:10and it did manage to write it well just based on the site's LLM.txt,
00:08:14but I still think I'm going to stick to normal React myself.
00:08:17The other downside is it feels like, especially for React,
00:08:19this is adding on more compiler magic on top,
00:08:21and it's also breaking flows that I've spent years learning,
00:08:24so it's personally not for me, but that does not mean it's bad.
00:08:27I think people coming from Svelte might like this a lot,
00:08:30and if you've used Ripple already, you probably already love this.
00:08:33So let me know in the comments down below if you do,
00:08:35while you're there subscribe, and as always, see you in the next one.
00:08:40[Music]

Key Takeaway

TSRX attempts to modernize component development by replacing expression-based JSX with a statement-based syntax that restores vanilla JavaScript control flows like if-else statements, try-catch blocks, and for-of loops across multiple front-end frameworks.

Highlights

  • TSRX is a new front-end syntax developed by the creator of Ripple that compiles down to work across React, Preact, Solid, Vue, and Ripple.

  • Statement-based JSX in TSRX eliminates the return statement entirely, rendering the component markup linearly in the exact order it is written from top to bottom.

  • The TSRX compiler bypasses React's strict rule of hooks by automatically hoisting hooks to the top of the generated function, allowing developers to safely write hooks inside loops, conditions, and early returns.

  • Scoped styling is native to TSRX via built-in style blocks that automatically append a unique hash to class names during compilation to prevent style leakage.

  • TSRX enforces double quotes for all bare text inside elements, meaning developers cannot write unquoted strings directly inside markup as they do in standard JSX.

Timeline

Introduction to TSRX Syntax and Framework Compatibility

  • TSRX is an extracted syntax from the Ripple framework designed to improve readability and co-location in UI components.
  • The syntax is backward compatible with TypeScript and integrates with major frameworks including React, Preact, Solid, and Vue.
  • A dedicated Vite plugin serves as the primary compiler step required to process TSRX files.

Developers have relied on JSX and TSX for years without major syntax updates. TSRX offers an alternative that co-locates structure, styling, and control flow in a single file. Setting up the environment requires a compilation step, which is handled via a Vite plugin or framework-specific runtime integrations.

Statement-Based Rendering and Keyword-Based Components

  • The component keyword replaces the traditional function keyword to signal rendering logic to the compiler.
  • TSRX utilizes statement-based JSX, which eliminates the need for return statements in UI markup.
  • Bare return statements are reserved strictly for early returns to halt subsequent rendering and logic.

Writing components without a return statement forces a linear, top-to-bottom reading order where elements render exactly where they are declared. This structure allows developers to drop markup anywhere in the component body. While this provides a highly sequential flow, it may require developers to adjust to not having a single, obvious return block to scan for the final UI structure.

Vanilla JavaScript Control Flows for Conditions and Lists

  • Conditional rendering uses standard JavaScript if, else-if, and else statements instead of ternary operators.
  • Standard switch statements directly render different JSX blocks depending on the active case.
  • List rendering replaces the .map method with an extended for-of loop that supports index tracking, stable keys, and continue statements.

React forces developers to use expression-based approaches like nested ternaries or helper functions for complex conditional layouts. TSRX simplifies this by allowing native JavaScript statements directly in the markup. For lists, the syntax specifically extends the for-of loop to handle React-style keys and indices, though it explicitly restricts other loop types like while, do-while, or standard for loops.

Error Boundaries, Async States, and Hook Hoisting

  • Native try-catch blocks serve as error boundaries and handle async loading states when combined with a pending block.
  • The compiler automatically hoists hooks to the top of the output code to prevent breaking React's rule of hooks.
  • Lexical scoping is enforced at the block level, allowing identical variable names to coexist inside different elements without conflict.

Error and async boundaries are simplified using standard try-catch blocks, which the compiler translates into lazy-loading mechanisms like React.lazy. For React users, hooks can now be co-located inside loops or conditional blocks because the compiler silently reorganizes them into a stable execution order before React processes them. Lexical scoping ensures that variables declared inside specific block scopes, like divs or if statements, do not leak into the rest of the component.

Scoped Styling and Component Text Handling

  • Inline style blocks automatically scope CSS to the component by appending unique hashes to class names.
  • The style keyword allows parents to pass scoped classes down to child components that accept class names as props.
  • Bare text inside TSRX elements must be enclosed in double quotes or written as template literals.

Scoped styling provides built-in isolation, meaning a child component with the same class name as a styled parent will not inherit the parent's styles unless explicitly passed down. To share styles, the style keyword ensures the generated unique hash travels with the prop. A notable syntax shift is that raw, unquoted text strings are invalid in TSRX, requiring double quotes for all static text nodes.

HTML String Parsing, Destructuring Reactivity, and Shorthand Props

  • The text keyword safely renders escaped HTML strings, while the HTML keyword parses raw markup in Ripple and Vue.
  • An ampersand prefix before props enables lazy destructuring to preserve reactivity in Ripple, Vue, and Solid.
  • Shorthand property names are supported when a prop name matches the variable name being passed to it.

TSRX introduces safety controls for HTML strings, offering an explicit text keyword to block cross-site scripting. For reactive frameworks like Solid and Vue where destructuring props normally breaks reactivity, prefixing props with an ampersand compiles the code back to property lookups so signals trigger updates correctly. Ultimately, while TSRX offers strong quality-of-life upgrades and appeals to Svelte or Ripple enthusiasts, its adoption face hurdles due to the heavy reliance on compiler magic and existing developer muscle memory around standard JSX.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video