Building Faster and Smarter: React and HubSpot CMS

John, Byron, and Anza walk through building with React in HubSpot CMS, comparing JSX to HubSpot's templating language and showing how React Islands add interactivity. They cover component architecture, GraphQL queries, and server-side data fetching with live code examples.
Sam.
Welcome.
How's everybody doing? So today we gotta talk about building faster and smarter with react and the HubSpot CMS. So in order to get started we have an agenda prepared as usual. So we start with a quick introduction. Then John Miller will take over with starting react development in HubSpot CMS. After that, Byron will take over and dive a bit deeper into advanced REACT techniques. And to put everything into one, we have Anzi who will provide some real world examples. After that, Byron will take the stage again with a quick roadmap. And last but not least, we got the Q and A. So you already saw all the amazing speakers in the intro, but here they are. And before we start, a quick info about the next events. So on the 7th of November we will talk about what's new with app cards or appcard components. And on the 21st we gonna have a roundtable discussion about developing leaders and building and managing developer teams. So you don't want to miss that or both events. So without further ado, let's get started.
Hello everybody, my name is John, I'm an engineer here at HubSpot working on the CMS REACT platform. And today we are going to be talking about jsx. The agenda is going to discuss the differences between JSX and Hubble and then we're going to look at a comparison of the pricing card module that exists in our CMS themed boilerplate and how we might convert that to CMOS React. I want to start with a little disclaimer here. So JSX is not React, React is a Library for building UIs and JSX is considered a syntax extension of the JavaScript language, allowing us to write this HTML like syntax within a JavaScript file. Even though React and JSX are commonly used together, they are separate technologies and JSX has support across many frameworks. So for example, JSX supported React, Angular, Vue, Solid and more. So with that in mind, we're going to start quickly by talking over what JSX is and the differences of Hubble in regards to it being a templating language. So let's get into it. So how is JSX different from Hubble? So for starters, JSX is an open source library and Hubble is our proprietary HubSpot CMS templating language. There's a lot of benefits with it being open source, you know, just to call out a few with it being, you know, community, sport, education, other developers working on it, on top of just the flexibility and the versatility to be able to use this beyond what we do. Just when you're developing on the cms, JSX and React are a component based architecture which starkly contrasts to Hubble, which would be considered an inheritance based architecture. You can think of that as, you know, a child template inherits from a base template, which you all should be familiar with that pattern. The component paradigm encourages developers to write reusable self contained components that encapsulate both your layout and your behavior. Which is it? Which is the big difference from the Hubble? The inheritance based paradigm, there's a JSX provides a declarative way to describe your UI which makes your code more readable and easier for others to understand. Similar to Hubble, CMS React defaults to server side rendering so your static htmls get rendered on the server. That's the same, the same in both scenarios. But with React to jsx you have more logical pathways to adding that interactivity to to your pages client side and the big win here is you're writing the same language, both client and server. So it's JavaScript everywhere. You only need to worry about JavaScript, which is a pretty powerful tool. And to lean into that even more, the world is your oyster. Since this is just JavaScript, you have the entire JavaScript ecosystem at your fingertips for using external libraries. Using the new APIs that come out for JavaScript, you have a much more flexible building experience. Okay, so this is the final product that we're going to be looking at today. This is the pricing card module that we're going to look at and do a little comparison between it in the Hubble world and then the CMS React world. So let's hop over into the code. One before I, before I jump over is that I'm going to gloss over a lot of the CSS bits. There's many ways to implement CSS within CMS React and there's a lot of different strategies and opinions. So I'm just going to. It's not really the point of this talk, so I'm going to gloss over that. But if you are interested in seeing the variety of ways that you can implement CSS, please check out our GitHub repo for a variety of examples. All right, let's get into the code. I'm going to start by looking at quickly going over the Hubble module. All this code should look familiar to you. So we've got, we're setting some variables here. We're conditionally changing appending these with some different strings at the end we've got some CSS that we're scoping to this module in particular and then our markup here. So all this should look very familiar. We're conditionally rendering things we have. There's some Hubble filters that are being at play here. We're looping over some data, all the.
Things.
You expect you're used to within the Hubble space. And then down here at the bottom we've got some client side JavaScript that we're injecting onto the page here. So this is the existing module. So let's start digging into what that might look like if you in the CMS REACT world. So let's look. So in the CMS REACT world we have the same concepts that you would expect in Hubble or within the cms, right? You've got fields, you've got meta information and then you have your markup within that scope of that module. So in the CMS REACT world we've got our export component which you can liken to the module HTML file within a module in the Hubble space. Fields JSON is same as this export of fields. And then we have our meta JSON which is the same export. So instead of 3 JSON file or 2 JSON files and HTML file, we've got 3 exports from our index file here, which is how we are getting that data in the right spots to run these modules and have them work the same as you expect. Let's dig into the fields JSX for a second. So as you all are familiar fields, JSON is how we currently describe the fields that the marketer will interact with in the page editor within HubSpot. And this is the CMS Direct way, the JSX way to do the same exact thing. So we have all the same support for all of all the field types that you would expect. We just do them in a different way. So we have, you can see we have all of our fields here and as I scroll down we'll show some style fields right here. So all these are our style fields that would show up in the style tab in the page editor. Now since we're using jsx, this is kind of. I want to do a little bit of refactoring here because I think this is kind of muddying up this file. I'd like to pull these off into their own file. So I'm actually going to grab these style fields here and then I'm going to create a new file called style fields and then I'm going to bring that over here and Then I just need to. Oh, now I need to import all of the things that I just removed from our fields. Sweet. Okay, so now we have our style field. We've got our new, our new style fields function over here with all those kind of pulled out of here. So let's now already removed it from this file. Let's go ahead and import it and bring them back into here so our marketer can use them. I'm gonna just put it here at the top. Style fields right there. Sweet. Typescript is compliant. There we go. So now this is the, this is just showing a small glimpse of the flexibility that we get with when we're working in an all JavaScript ecosystem. I'm able to pull this out into its own function and import it, which if, you know, use cases. If we had style fields that we wanted to share across multiple modules, we can now organize in a more logical way and reuse the same code instead of copy and pasting everywhere, which is pretty awesome. Okay, let's hop back into this index file and let's look at this module, this component or mod or quote unquote module HTML from the Hubble world. So if we look through here, we're setting some variables and we have all of our markup down here. We're conditionally rendering just like we were in the Hubble module. But again, I think there's some opportunity here.
So this.
Card header that we have, I think we might want to use this again down the road somewhere else. So I'm going to go and pull this into its own component as well. I think we'll call that cardheader. And then I'll hop over here and I'm going to bring all this over and I'm gonna create my cardheader. So I'm gonna do the same thing I just did, except now we're going to be pulling in some props. Let me return this stuff. I don't need that or that right now. Import our rich text component. Okay, so now the style fields was a simple example, but it doesn't take any arguments. So. Oh, we're actually taking props here. So I'm gonna, I'm gonna pull in the props that we need because if we don't have tier or description available, if the marketer does not set them, we need to, we need to not render this module at all. Right. So I want to do something like this where if we don't have these things available, let's make sure to, to return nothing. And if the end description are available, then we're going to render these items and then I can go back here and let's drop it in. We have cardheader and then we are need to import it. So now we have this imported up here at the top, this component and we are reusing it here and we're passing down the props that we are expecting that you can see here. And now we're using to initially render this markup here.
So this is a.
Now I have this component, you know, in a separate file. So if I ever run into a situation where I need another, I mean, this card header or this tier description rich text experience for our marketers, I can reuse this card header in those other places very easily. Let's do one more here. I think this overlay doesn't really need to be in here. You know, this overlay could totally be reused again. I showed you in this one one way to conditionally render like this. And I'm going to keep this way now so we can kind of see both examples in here. So let's go here. I'm going to pull this out and create my own card overlay component, kind of the same deal we're going to create. We're going to do a function here called card overlay and I'm going to return this information. This markup here. We're missing a few of our variables that were set at the top. So let me go up here. We've got these three set here. I'm going to bring these back over into the cart overlay and then I also need to import our close icon, which is an svg. It's a component that we're importing over here. So let me. I'll just show you that real quick. This is another good example of being able to reuse the component. So I just have this simple X icon that shows up in the iframe that pops up when you click that button in the pricing card module that I'm importing into my overlay so that it's there. So you just are able to click out and close that overlay when they want. Okay, this is looking good. Typescripts mad for a second and then let's go ahead and import this back into our file. Great. So now this file looks much more manageable. We've given ourselves some more flexibility with our cardheader and our overlay being in separate files. If we see is this running? Let's hop over here. And it is broken as most things are. The choice field is not showing up. There we go. This should be Happy now. Great. Now we have our module rendering locally. Everything is happy. It's looking how it's supposed to in the local environment. Great. There's a couple more things I want to do. If you look over here like this, this dollar amount here isn't showing the currency so. Or the USD or whatever the. Whatever the right currency is for this module. So the normal way to do this in Hubble would be to have the marketer put in. They can go into the fields and then add their dollar sign as a text string next to their the number they put. But I think it would probably be cool since we're working in JavaScript, we might as well just import a library to handle that for us so the market doesn't have to worry about it. So I am going to, going to import a library. Let me turn on, turn this off for a second. Do this. I'm going to import a library called Currency JS which will handle some of this for us. And we're going to hop over into the card body which is here.
So.
Card body. Let me, let me close this out for a second. So we've imported our package, but let's go and look at this a little bit. So cardbody is the middle part of that component. We have a few components that are nested within that component. Another call out to why the component architecture is so great. So we have these three components that are living here. I have them in the same file for now. That is totally something you can do. But you can also move them into a separate file just for demonstration purposes. I kept them in here. So we scroll down to where this price is. So we have this here. This is where our price is being rendered. And again this is just a string, a text field that the marketer is putting in our number field that the marker is putting in a value. So we want to go ahead and auto format it for them. So we've already installed our package. So let's import currency from currency js and then I want to use this down here. I'm going to create a variable for this called formatted price and this is the, how we would call that. We call this format method from currency and we pass in the price variable and that should work. And then so that's running. Let me run the dev server again and see if that's working as expected. Hop over here. Great. And now we have our currency or dollar, our USD dollar sign being added. So we're good to go. Now the marketer does not have to worry about it. Okay. So now we have pretty much, I think our components in a pretty good shape, right? We have, we've got this organization here, we've got our library handling the formatting. So our marketer is happy. Let's go look here and I'm going to click this button and it's not really doing anything for us yet. We need to add, this is a good opportunity to add some of that sweet, sweet JavaScript interactivity. Which means it's time for Byron to set us up and make that happen. Over to you, Byron.
Hello. Thank you John for all of that. Yeah, it's perfect segue to talk about how in CMS React we add interactivity to our websites. Our front end implementations backing up a little bit. We're going to talk about some of the more advanced topics in CMS React, this concept of Islands and then getting into some data fetching concepts. So first a little bit on why Islands is a thing. So as we saw with the module in the beginning, the old Hubble module, JavaScript isn't integral to the actual components themselves. It's like this separate concern. Also those JavaScripts can be included in modules. They can also be. People will include them in the head or maybe they're in a template include somewhere and then and so it can become difficult to reason about and cluttered and sort of fragmented in terms of where the JavaScript is coming from. And also it's on the developer to manage like instantiating the JavaScript. It's going to be a window onload or whatever that's going to be. So Islands is a way for us to make that more streamlined for the developer. So it's just an afterthought completely. They just write their React components and if they want something to be interactive they put it in an island and it just works out of the box. And then some other reasons why Islands we think are a way to remove quote foot guns from our developers. If JavaScript is implemented in an inefficient and fragmented way, this various scripts can cause blocking and that blocking behavior can have a negative effect on some core web vitals. The tenable plate, the first input delay and time to interactive. You use CMS React, you use Islands, you don't have to worry about blocking at all. We've even made some further optimizations on how Islands load to do better. That's just specifically what an island is. First, if you don't use an island. If you just use the stuff that we were looking at with John where he's writing his components to CMS from React. We're not going to ship any JavaScript to the front end. Everything is going to happen on the server and it'll just be a server rendered HTML. It can pre render the same way Pre rendering works today. Super performant. But if you using a component in an island we will automatically ship react to the browser as well as a little bit of code that knows when to hydrate the component. Hydration is a react term and it. It's the process of taking the server rendered HTML and making it fully interactive by adding event listeners and reassociating the react components. Hydration because the HTML is the same on the server and on the client, it doesn't cause layout shifts, which again is another core web vital concern. With that I'm going to jump into some code here. This is essentially where where John left off in his component. We have this in his talk, we have this index component in the pricing card and I want to make interactive the card overlay. I have a card overlay component which. This is the static, this is the non interactive version that we were just looking at. I just so happen to have an island based one. I'm going to get rid of this existing card overlay and bring in my island based ones. Let me get rid of this.
Bring.
That in just a little on mechanics here. There's this island component that ships with HubSpot CMS components. You need that. And then when you import your island, this is the syntax you need to use. You point to that file like you add another import and you add this question mark island flag on it. Then when you render it, you render the island component and you pass there's this prop module and you pass the cart overlay. Then if we go look into card early, I'm not going to go through all of the nitty gritty of react interactivity and the way you add handlers. But you can see for the most part like here's our button, it's got a handler on it. On click handler. But notice all I've done is changed it to an island and I have this island component and I'm going to assume it's going to be interactive. Let me jump over to my demo here. Refresh this up. And now if I click on this Pokemon, its interactivity is going to show up. That's all I have to do in terms of going from it's just rendered on the server to now it's on the client. Then you can see in the network. Once I've done that, there's this island runtime JavaScript file that gets shipped to the browser automatically. Developer doesn't have to do anything. What's in island runtime just demystify is it's just a specific version of React that we're pinned to as well as the logic to then pull in the the client side React components and render them. So that's why you'll see these imports of the card overlay is the island in this case and then another example. So that's. Let me check my quick here on Islands and you can see also something that's nice to have is if hsdebug is true in the. In the URL here, we're going to be logging out when things are hydrating so you can actually track the hydration. And I'm a little pressed for time, so I won't go too deep on this. But there is a way to. There's this hydrate prop. Hydrate on prop and that takes idle load or visible and idle will wait for an idle cycle in the browser and then it will hydrate. The island load will just as soon as the page loads, it'll hydrate it and then visible will make it so that if you have a module that's down the page somewhere, the island hydration won't occur until that is visible on the page. That's some optimizations we have there. That was a quick run through of some island stuff backing out of that into talking about some data fetching things that we have available. So first GraphQL GraphQL is available now this is for enterprise only though, and you can attach GraphQL queries to modules and templates in the sort of classic Hubble world. And the same thing applies for React modules. You just export a GraphQL query from your React module and then that data will get passed into the props of your component. And then there's also Hubble has quite a long list of other things that you can potentially load. One mechanic we have for getting that information into React components is this thing called the Hubble Data template. This is one of those transition areas between the Hubble world and the React world. You just export this Hubble Data template from your component file and you can just return this Hubble variable called Hubble Data. You can in this case we're pulling some information from a blog and then you can reference that in your component via this is props Hubble Data namespace. It's a great way to get things like site settings, anything else you might be pulling from Hubble and want to get into your react components. Then there's something which is quite novel to the react space that you cannot do in Hubble, and this is this thing called getserversideprops. And I'm going to demo some of that now back to my same file. If I want to use getserversideprops, what I do is I just export this function called getserversideprops. Then we ship a host of these helper functions. This one's called with module props, but we have with query, with URL query and with contact. And it's a progressively more robust amount of data that we pass into the getserversideprops function here. And that has implications on caching and some other things. I'll talk about that later. But so this is a basic example here where this. And what will happen is this getserversideprops function will execute at render time on the server as the page is rendering. In this particular example, we're going to make a API call to this currency conversion service. I have a private key that I'm using and getserverServer side props can be used with HubSpot secrets. You can put your API keys and your secrets and you're able to pull them and use them here to make API calls. This shape here at the bottom is the shape that you need to return from your getserversideprops function as a server side props key, a caching key. And then within the caching key you have cache control. And the cache control key is important and it basically matches the spec of the cache control header. What this does is it controls the caching at the module level. In this case, this module will cache for 10 seconds. This also will cache based on the module props that are passed. If you go to your page editor and you change some of the field values, that cache will become invalidated. And there's a new caching key based on your module props. Just a little bit on how this caching is where it's fairly robust and fairly configurable. That's my getserversideprops function. Then what I'm going to do is server side props is going to come in and I'm going to do a little bit of refactoring here. I have this. Currently card body is coming from just the standard place, but instead what I'm going to do is pull cardbody similar to how I'm pulling the card overlay. Yep. Like that, like that. And.
Wait a minute.
Live demo, folks. Oh, that's right. Okay, yeah. We're going to pass this into Card body, then inside of Cardbody. So I have my island in the wrong place. I have a different island in here and that island is this price display. I'll show you what I'm going to do with that. I'm going to import a price display island and get rid of this inline price display function that I had and then render Price display as an island. I have my price display island already set up here and what it's doing is, remember at server render time I've made this call to get exchange rate information. And so what I'm doing now is I'm changing my module to have a little selector that'll be able to. You can change the specific currency that you want and it'll just do the currency conversion for you on the fly here. So see here.
Pricing card.
I might have gone down a dark path here and back this up a little bit. I think for the interest of time I'm going to jump here and it's a bummer. I'll see if I can get this hooked up. So on the other side of Anza's presentation we can just take a quick look at this. So I'm going to leave it that. But essentially what this is going to be able to do is have this price display, this currency changer and that will just update it real time based on that API exchange call that we made. But I think for the interest of time, I really want to pass it off to Ans, give him a chance and we'll come back and take a look at this afterwards.
Thanks Byron. Thanks for a nice introduction. No issue there, we're definitely going to get back to you on that. But for my presentation we're going to talk about something a bit more advanced and that's how to actually use HubSpot's React CMS in production apps. We've got a great example here that we've gotten approval from from the client where we worked this on. And we're going to showcase how we made a really complex React app using Household cms. So what Household React really allows us to do compared to Hubble is a couple of things and most of them have been mentioned by Byron. But I really want to quickly go over what makes sense for us at least now. The first one is definitely to build dynamic data driven components that have really flexible schema definitions, seamless server side rendering with island, and then of course server side data fetching. With the getserverside props we can create interactive user experiences and then we can also leverage HubSpot's backend for solutions like Auth, and that integrates smoothly with the content management and CRM data, which is the nice part that Hubble already offers. So what are we going to do in our specific application? So the major point that we're going to go across is using UI extensions as custom react cms fields using third party APIs and the getserversideprops function using CRM data for dynamic content, so dynamic pages. And then ultimately we're going to be building membership page models. So here's a quick demo of what we're going to be building. This is the end product as it looks, and it might help you to see what we're building just to see the end result first. So to glaze over, the client is a builder. They have a lot of designs that I want to showcase and want to create an interactive module on the front end that lets users configure designs, configure floor plans, interact with them, save them, print them, and so on. So this is all their designs on the front end. We can click into individual ones and view elevations and then we're onto the actual design configurator. Keep in mind, this is all in HubSpot and everything is made editable by the user. You'll see that some modules, some options even get locked. They have relations. Not all the options are available with every option. So we need to also have some few little hooks in place and we're going to go over all this in this demo. So there's one major issue here that perhaps helps what CMS can solve. And for us, that was building data that isn't linear. So you'll see that in most CMSs, the actual issue is that CMS data tends to be linear. Field one is always standalone and so is going to be field three. We have a couple of nice primitives such as strings, booleans, enumerations, but end of day it's very hard to create field level validations, especially when we have fields that depend on each other. Another big issue is we can't have objects and JSON data is currently not supported in HubSpot, so. But our app in this case needed nonlinear relational data. In our case, field five depended on field one and two and this was just not possible with regular CMS fields. So for that purpose we used UI extensions to actually create the data that is going to be consumed by the CMS app. This is how that looks when it comes to the actual CRM view. You'll see that we have a nice UI extension on a custom object where the user can configure specific elevations, specific floors and then specific options within them. All this is made fully custom and we're going to quickly go over the UI extension as well. So the biggest point with UI extensions for us was client side validations. Clients tend to be very messy when it comes to entering data and it's important that we keep a type grip on what they're able to put when it comes to data. So for that purpose we have some predefined drop down options, field level hooks and then also collection level hooks, which just means hooks on events like the save event. This is how it looks on a front end. So we give a user a quick dropdown to choose which specific community and home they want to integrate with. And afterwards this is how this looks in the actual code. So it's a very simple fetch communities function that runs our serverless function and gets us all the available communities and populates the state. We're going to go in a bit more detail as well as and the end of it. This is what was available in the video, but to give you a quick look on it as well, this is how it looks at the end. You can pretty much choose what you want in terms of elevations, in terms of your floors and then in terms of your options. This look is a bit different than what was in the video as it's an older version, but essentially UX ascensions allow us to do very, very complex data. And at the end the goal is to get something like this. It's a very nested data set that is very dependent on fields being properly validated, not having the same option labels, option journal names, IDs and stuff like that. So it's very important that this sort of stuff was heavily regulated on the client side so that there was no errors when it came to input. The end result was a bit of a data mess because we ended up firstly storing this data in the actual custom properties. This was a great idea because HubSpot hosted the actual data for us. No need for an external data bank. But this quickly became a big mess because we ended up having to split it across multiple different properties. This is how it looked like on the JavaScript front. Not very nice, but still at least manageable. The real problem came when it came to the CMS side. This is how it looked in the actual Hubble data template and you'll see that it's quite a big mess. There's also some concern with how much Hubble can really handle and is this production ready? So we ended up going for a different approach here, which was a database sitting on a custom server. So the uxensions interacted with a backend platform that opened up a couple of endpoints for us and that platform interacted with the actual database. In here you can see how this looks like it's a very simple database with a couple of columns. We have a production config, the integration config and then our user modifications so the user can override any integration data if they need to. This is a couple of images from how this looks like. This was viewable in the video before, but pretty much they're able to connect a specific model and then this model is pretty much linked to the model in their ERP and they can make changes, resync it, unsync it and stuff like that. Ok, so we have the data in the actual UI extensions now and now we need to get it into the cms. Byron showcased how he does this with getserversideprops and that's pretty much the mantra. When we get to this you'll see that we get the actual object ID of the design passed from Hubble data props, which is very ideal here. Afterwards we just use a simple fetch API to get it from the server and then put it across the server side props. The very nice thing also is the cache control because we don't expect these designs to change on a day to day basis. It's really great that we can put a cache age, especially a long one because it really simplifies and speeds up the performance, especially when we're dealing with large amount of data like this. Okay, so we have the data, we have the CMS fields, it's fully type safe end to end. Now let's start building membership pages. So building the actual app, there was two requirements that we had to do. The first one was a nice client side facing public configurator that anybody can go on. And then the second one was a closed loop safe floor plans that only the people that had been invited can view, interact, save, share and so on. So for that purpose we decided to go with two different modules. The first one being a regular four band builder and then the second one being the safe floor plans builder. So to go into the components we had to decide what made sense to be used together and then what was custom for each module. In this case it makes sense that I guess the component that decides that lets us edit the elevation is going to be a separate component. Which is ultimately what we went for here with React. I frequently like to enforce a context pattern. Data drilling is problem drilling is a big issue when it comes to complex apps. So for this purpose we decided that the Edit Elevation component is going to have its own hook called the Use Floor Plan Data. And whenever this hook is wrapped around the parent, you can use the Edit Elevation component freely in your application whenever you want. All this data will of course be showcased later on and this presentation will also be available for everybody to look at. All right, let's get to the actual Edit Elevation component, which is the centerpiece, and we're going to dive into the code a bit later on the front end. It's actually a very simple markup. We have a couple of predefined components here, but the majority of the work actually gets done by our floor image component and then our actions, which are hidden. Over here we have a couple of actions that let us do stuff like get the estimate by submitting a form, printing elevations, saving the existing one, or saving it as a new entry. This is for logged in users. We also have a principal floor plan component which just injects some regular CSS to make as a regular web print file. Now saving changes to the CRM is also very simple. This is something that I think Byron didn't showcase, but you can essentially interact with the serverless functions after the fact. So the first function was the getserver side props, which lets us get data on the server and then push it to the client, which is great for web vitals. But then another important part is also being able to interact with service functions on the go as the users interact with. For this app, this was a very important use case. And serverless functions can now also be kept in project folders, which is very great because it's very easy to use them, have them in the same repo, use the same pipelines and stuff like that. In this case, very simple logic. We get the ID of the actual contact that's logged in and afterwards we create a new floor plan for them. So how do we make the actual module itself? Well, the grunt work still has to be done by the Hubble Data template. Unfortunately, HubSpot CMS React is not the first thing that HubSpot fought off. So a lot of the underlying methods are still locked behind Hubble and this is really not that big of an issue because we can easily just get them in the Hubble data template and they can later on even be consumed by the getserversideprops function. So in this case, we are using a dynamic CRM page because we're working with custom objects. So for us we first have to check if we're on the dynamic page and if this page is for the design selection, where it's for individual designs already. So for this purpose we're doing a check if we're on the general page and we get all the designs or if we're on the individual page and we're looking to get just the individual design afterwards we just make sure to set the correct variable and then the rest is already handled for us on the component. It is very simple. We have the elevations which we defined before in our server side props function and we're already inferring it as the props we're expecting to get over afterwards. We have a nice little hook called Use Elevation Sync which pretty much just lets us keep the current elevation inside the query params. That's because HubSpot doesn't allow us to create nested pages as it is still a traditional cms. So after we're already on the design one page we have to use query parameters to do anything further on. This was a very nice feature because you were able to have users copy the link and send it across to their friends and still be able to view the exact same data afterwards. We're just using our predefined context hook which we talked about before and checking if we're on the index page. This is a bit of an unconventional solution, but we figured out that if there is no designs passed to the Hubble data, this pretty much means we're not on the index page. Right here we have a get is deployed hook that's pretty much just for local development. As a local development, we really have no interest in viewing the index page on the membership side. It's very similar. So the membership function has a very similar pattern. The only real difference is that we really need to make sure that we have the logged in user here and that we're also getting all the saved homes the user has. Since the custom homes are a custom object, so are the saved homes pretty much. We know a user owns a home if it's associated to them and afterwards we just fetch it with regular Hubble functions. In theory we could also do this one with the getserver side props, but we just decided it was a bit easier to stay with Hubble here and then pass it along in the saved floor plan builder component. It is very similar stuff. We're using the same context hook that we have right here inferring the data as it comes. And afterwards we're plopping in the same components for editing the actual elevation. Then we have our other component for actually checking over all of our sales floor plans. And that's pretty much it for the high level view. Now we're going to be taking a look into how the app looks like and how we've actually managed to approach this. This is the app as it stands right now. So a lot of it you've already seen in the presentation. But to quickly go over it, I like to enforce a pattern where the index is really is just for data fetching and defining the general stuff like the metadata and the fields and then the rest of the stuff live separately. I think I saw this in Brian's file as well, Byron's file. So for us this usually means that we defiled a root level island because this is a fully client side application for us. And afterwards our floor plan builder lives in here. You will see that we're using this really nice use elevation sync hook and I just quickly want to go over how that looks. But essentially all we need to make sure is that we're getting the query parameters, checking if it's initial render to stop any potential loops from happening. And afterwards we're also pushing all this data into the history state so the users can go back and aren't met with countless cycles of being pushed into existing elevations. The most important part of the actual application for us is the edit elevation component. This one was a bit tricky to make, especially because the data was very complicated and hard to deal with. And we're going to go over it. You'll see that we've put a nice console log in here. This is just for showing what the data actually looks like. And if we go to the client we'll quickly go over how this looks in the front end. So I did a nice console log out here and you can see that we're currently sitting in the Syrento design in Elevation 4 and this is our console log. We have our data in here and I usually always like to use stuff in Chrome as it visualizes the data pretty nice. So we'll go over one of the floors and let's say we're going to go with the first floor as looks looks relatively nice. Of course the React V render and there we go. We have our option menus right here and this is what is rendered on the left side here. And then we have our images. What is detrimental to really important to realize here is that actually one image isn't always assigned to one option. As if we select the Garage two, perhaps the dining room is no longer an option, or perhaps the dining room might look a bit different if we're using Garage 4 or if we're using Garage 2. For that reason, each image has its own condition and this is something that we get passed along from the actual erp. The conditions look like a very similar, very simple angular readable logic. So it's using and, OR and not statements. And based on that we can pretty much configure the rest ourselves in react code. If we go back to the tab, you can see that in here we're firstly finding the floor and then we're finding the selected floor the user is currently on. Afterwards we're creating another hook called use ActiveOptions IDs, which pretty much just stores the options we currently have selected in an array so that we can use it later on. And afterwards we're filtering out only the web enabled option. As you could see, as you probably saw in the UI extension before, we're allowing the users to disable some options temporarily. The main part of the code is actually the toggle option button and toggle option function, which is extremely complex and really hard to go over here. But it's a plethora of choices that pretty much takes in account all the different parameters the client want to hear to see how each fields are dependent on one another and which fields are showcased in which example, which is why this is a bit hard to go over. But we're going to focus on the other stuff which is showing the actual images. So we have this four image component that you saw in the previous example on the image, and essentially in here we have this really nice helper function wrapped in a use callback hook that is going to take that expression and evaluate it continuously until it evaluates all the expressions individually. So we're doing a quick regex match, which of course we had with the help of AI. And afterwards we're pretty much checking each expression based on what it is. If it's an or expression, if it's an and expression or a non expression. We're continuously looping over this function until we pretty much get a final statement which we evaluate. Down here we check what the option key is and what the option value is, and then we're evaluating that against the current option IDs. Afterwards we're running this should display image function for every image on each re render. And at the end we're left with a very nice application that pretty Much does all this for us. So you'll see here if we select the morning room, this will change it on the first floor, it will change it on the basement. There we go. And if we unselect it, it will also, let's say, remove it on the basement. In some cases you will see that perhaps the morning room works a bit different if we have the covered porch or not. So in this example we're going to pick the morning room and the covered porch and they look like so. But if we unselect the morning room, the covered porch also looks a bit different. This is all thanks to our engine here that is going to recursively check on each tree, render every expression for every image at the end. We also have some functions so the user can interact with the floor plan. The most important one for us was the get estimate form. A lot of clients really want to know how much a building costs, especially when there's no prices displayed. So for this example we the client was able to submit a simple form and this ended up creating a request form submission on the actual HubSpot side pretty much natively. So no difference at all to the client. And this has been a big ask for a lot of developers as well is how do you make actual forms. So to quickly go over that as well, we'll have a look at how this form is made. Now, I'm a big fan of the shadcn library, so I try to use it as much as possible and nothing different here. So we're using the shadcn form component, which essentially is just the react headless form that we're importing right here. So there we go. Use form from the REACT hook form. We're defining all of our fields in here and then we're creating a submit function. So for us, we decided to open up a serverless function that essentially takes in all of our data and submits it for the secure HubSpot react form. We're passing along the cookie data, the page information in all the fields and if in cases where the floor plan ID isn't already existing. So let's say this isn't a safe floor plan and I'm a new user, we're also going to make this floor plan and associate it to the user so that the sales rep know which floor plan to actually give the estimate for. We also have some nice toast in here provided by of course shadcn. And this is all managed by a popover component that's also provided by shadcn. Another great reason why React CMS is so powerful is because we can really use any components and anything else that's compatible with the React ecosystem and that's pretty much it. We can extend this app however much we want. We have all the stuff predefined, all of the hooks, all the components, the state, everything is perfectly managed and then we can just pop on components as they come and we can pop on feature requests as a client wants them. That's pretty much it from my side. Popping it back to Brian for a final closing arguments so he can give you some more details about what HubSpot 3 actually is going to have to offer in the future.
Awesome. Thank you so much Anze. Just a well of information in that presentation I think. So it's really fantastic to see across the hubs and using all the different features. Yeah I was successfully able to unbreak what I had been working on so that's good. But something I wanted to actually back up just a little bit here before I say anything further. Just a quick disclaimer on some of these forward looking statements I'm about to make around what we might be shipping in the future. The short of it is everything I'm about to say hopefully will happen, but no in stone promises. So something that was happening while I was doing my demo is that we had shifted to CMS Dev server and the CMS React stuff. But what I was actually working on was this thing that we're calling Unified Themes which is essentially you're able to work locally on a full theme with all Hubble and React mixed together, work on those things locally and then ship those in a project. No need to go into the design manager. All of your previewing can be done locally and everything ships together. So just to prepare, prove that out. If I go into my cursor component, go to my base Hubble template here and I'm just going to uncomment this little header I put in my base. We go back to my dev server and you can see my header is updated. So I just made a change in my Hubble file that's along with my React files. I'm able to see those things locally and just to prove even further what's going on I can go ahead and I can deploy this to production and so I can make all of my changes locally, preview everything locally and then ship one atomic change that's going to do a build and ship that product to production. So while that's. While that's going on in the interest of time other things we're looking to is to just, you know, the current dev server is relatively bare bones here it's showing oh in the new unified world, not only can you look at your modules, but you can also look at your Hubble templates and any other Hubble modules that you have. This is a dev server improvement that's going to be happening. We're workshopping right now a whole host of improvements we're going to be making to the dev server to make this really your CMS developer home. Listing out your projects, being able to search for different modules, seeing dependence of modules so what pages are using them, being able to preview from those things. These are all things that we're working on currently. Just to check back into my build looks like it was successful. If I go ahead and check this out. Not in my local host anymore. I can see that all my changes went ahead and made it production. Also my friendly little converter here is working. That's some on the devset improvements and last but not least, just want to something that we're really excited to be shipping very soon is what we're calling modules and partials. So essentially the one larger size building block in the REACT world you'll be able to have, you'll be shipping D and D REACT tags. So you can essentially not entirely React template but maybe a little bit smaller than that. But you can create this thing called a React partial. You can use D and D areas, you can put modules inside of that. Much more powerful in terms of expanding the surface area of what you can do with CMS React. With that there's only a few minutes left but I want to offer some time for Q and A. So I'm all set on my side.
Thanks everyone for the amazing input and demonstrations. We do have a few questions available. I think we should start with the most important one and that is what is your favorite part?
Pokemon.
And I think that's one for everyone here.
Oh, I put in a stack. It's the Clefairy.
That's a great.
This make me happy? I don't know, I think I'm a.
Bit basic but for me it has to be Charizard. This looks so cool man.
That.
That's definitely a classic. How about you John?
Jigglypuff all day?
Yeah, great answer.
Cool.
Okay, a lot of the questions came up during your demo and say. I think the first question that we have from Mark is regarding how the output was saved. However you answered that already during your demo. The second question that was a follow up question was if a custom object could have solved your use case for the using a off platform database and if a custom object could have solved that for you by using that instead. Is this something you thought of?
Yeah. So for us this actually is a custom object. So this client did have a design set as a custom object and we could like in theory we could store this in 100 different properties and this would give us enough buffer to never worry about it. But when you think about it, is this really the right solution for us to go? We knew this client is going to have more work done, there's going to be more feature requests, we're going to need more extensibility. So at that time a postgres database really was the only solution for us to go. Perhaps something like a MongoDB would also not be bad as this really isn't relational data. But we did think we are using a custom object and for us it just wasn't enough in this case there really is no other place to store this amount of data.
Great answer. Another question that we have from Robert also during your demo is he was wondering if you somehow are true tracking the configuration of the floor plans by using other HubSpot tools that are available in CMS like Custom events. Is that something that you're doing?
Great question. I really want to do this with Custom Events. At the time this wasn't a client priority unfortunately, so we didn't get to it. I think it has been in the making in the thought process. Custom Events would have been a very cool thing to track client progress as they spec this out. A very good point.
Another question by John, he was wondering if the popover that was available in the demo is using the HTML popover functionality.
It's not. It was just two experiments at the time. I think the HTML popover is still even under only under experimental flag for the Chrome tools. So the support for it is like I think 2% and chatcm was a library we chose at the time. It's pretty much phenomenal. Nothing I have against it. We use every component that we have in this project with shadcn so it was the logical choice.
Nice, nice. And final question for today from Astron and I think this is coming from because it was such an amazing demo and a lot of things were happening. How many people did it take to create this masterpiece?
It was mainly me, but at the end I did get some help from a colleague here that's also in chat. So big shout out to Kilian. He did a good work on this as well. But it's not that big of a project when you think about it. It's just a lot of hours that take time. But I think a one to two man team can definitely achieve this if they put their heads to it.
Insane. Insane. Really good job. That's a wrap for today, so thanks a lot for joining us and sharing that brilliant information. If you want to subscribe to the developer arc, hop over to events.HubSpot.com developer developer and all the future events will be available there as well. Thanks a lot for joining and have a great day or evening.










