Code with Confidence: Best Practices for HubSpot CMS Developers

Dennis Edson, Aaron Wiggers, Ashley B. Webster, John McLaren, and Thun discuss advanced HubSpot CMS development techniques. The panel covers DevOps workflows, theme structuring, performance optimization, and security best practices for developers building on the platform.
Welcome to the newest developer hack event. And today we gonna talk about code with confidence, best practices for HubSpot CMS developers. And before we jump right into it, just a quick update. The next event will be on the 25th of April and it's gonna be about the power of HubSpot quote templates and the API integration. So the registration will be open shortly and so let's dive into the hug. So today we have amazing guest speakers. Aaron Wiggers from Bridge Ref, Ashley B. Webster and John McLaren from HubSpot. And Dennis Edson on the mic and the control center behind Thun couldn't make it today, but we have a recording of his part. So yeah, let's see the agenda. We will start with Ashley who's gonna talk about the breaking news in CMS development. After that, like mentioned, the recording of Toon, then macros and data filters with Aaron. After that I will talk a bit about structuring your theme and how to get the most out of it. And last but not least, CMS security and performance with John McLaren. So.
Yeah, yes, I still would have to refresh the page each time I have synced my code or made changes in my code. But still, it is a really nice way to have a single portal for HubSpot CMS development. But what would be the best way to sync our code to the staging environment? And this is where it gets interesting because we could use the HubSpot CLI and we could use the watch command, but that isn't the best DevOps flow you could think of. If you are using BitBucket or GitHub to have version control and store your code in, you might want to have a setup where the code is synced to either staging or production each time you push to a specific branch. And depending on your git flow, that would be either a develop branch or the main branch. Or it could be a feature branch if you are working with a separate git flow. Now the question is how would you use that by leveraging tools like GitHub, Actions, CircleCI or DeployHQ by using the HubSpot CLI. Well, let me show you what I have come up with. In my case I am working with CircleCI and each time I have a BitBucket repo where I include a CircleCI config, I can connect my BitBucket repo to CircleCI. Each time I'm going to push to a branch in my BitBucket repo, circlevci will start running this specific config file. Now let's Take a quick look at this config file. In my case I am loading a Docker image, and in this case I'm specifically loading a node image. I am defining my working directory, which is my repository. I am going to check out the code that is in my repository and I am going to install the HubSpot CLI. Now after that I am going to install the node modules that I have and then I have a little snippet where it gets really interesting. In this case, I am setting HubSpot environment variables. Normally when you are syncing code to HubSpot, you would have a HubSpot config file and you would use the HubSpot CLI to sync the code to the portal that is connected in your HubSpot config file. However, what you do not want to do is push your HubSpot config file to either GitHub or BitBucket because that is a massive security issue. HubSpot CLI does offer a really nice flag that you can use to listen to environment variables instead. So let's see how you can utilize environment variables to push your code to a specific HubSpot portal. Now, if we take a quick look at my CircleCI project settings, you can see that I am allowed to store environment variables. And in this case I am storing a HubSpot production ID, which is my HubSpot portal ID, for my production environment, my production portal. I'm storing a production personal access key specifically for my production portal again, and I'm doing the same for my staging portal as well. Now if we take a quick look at my config, you can see that I'm looking to the branch that the code is being pushed to. So when I'm pushing code to develop, I'm loading the the staging environment variables from CircleCI and I'm storing them in the environment for this specific deployment. If I'm pushing to the master branch, I'm doing the same, but I'm using the production environment variables instead. And if I use the CLI command called upload and I'm adding the use end flag, my code will be deployed to either my staging environment or my production environment based on the branch that I'm pushing code to. So this is really cool. I suddenly have the option to deploy my code to a specific portal based on the branch that I'm pushing code to. And by doing so, I have a really nice strategy to set up a DevOps flow for HubSpot CMS. One of the questions that might pop up right now is how you can keep your staging environment and your local environment up to date with your production environment. Because your production environment might have a lot of website and landing pages that you want to check and see if everything is still working once you have pushed your code either to your staging environment or if you have synced your code to your development environment. Now if we take a look at the HubSpot blog that has a really nice functionality to import and export a blog based on a different portal. However, that functionality is lacking for website and landing pages. However, there is a way to work around that by using the HubSpot API. So let's take a quick look at how you can solve that. In my case, I have already synced my code to a specific portal and that is my demo portal that I have used quite a bit in the past. However, you can already see that a page that I want to have available in this portal is lacking and that is the homepage of our website. Now, if I want to somehow migrate or copy over this specific website page to my staging or development portal, I would have to utilize the API. That is however, quite easy in my case. I'm going to make an API call to the CMS Pages endpoint that is available in the HubSpot API and I'm going to pass it a page ID. If I make an API call without passing the page id, I get all the pages that are available in my production environment. However, if I do pass a page id, I get the result of a single website page. Now, this contains a lot of information, but it also contains all the content of my website page. And if I copy over the response that I have gotten from making this API call and I would post it in the body of this specific API call and this is a post API call to again the pages site pages endpoint of the HubSpot API, I could try to create that specific website page in a different portal. Once again, I have already synced my code to my portal, so the modules and the code and the templates that I have available in my production environment is also available in my staging environment. But if I click on send we might get lucky. Ooh, we get an error. But don't worry, that is because in the API call where I make my get request, I will also have a few date variables available that we do not want to include in the API call once we are creating a new page. So I am going to simply look for those specific params and I'm going to remove them. And if I click on send Again I get a two way, one created response. Now let's see if it actually worked and I'm going to refresh my page and boom, there it is. My homepage of my production website is now also available in my staging or development portal and if we take a quick look you can see that there is some stuff missing. But don't worry, this will allow you to have your website pages available in your staging or local environment and you can make sure that you have all the necessary tools available once you are deploying your code to either of those environments. Thanks a lot for listening and I am really sad that I wasn't able to make it. But don't worry, all the other speakers have a lot of good stuff for you as well. Have a great day.
So I think Thun's part was amazing and let's give it up to Aaron who's going to be talking about macros and data structures.
Yeah, awesome, thanks. So I'm going to try to go pretty quickly here on my topics because I can be very long winded and I frankly love talking about this. So I'm going to try to keep this nice and tight to 10 minutes. So what is Hubble? Hubble is kind of a best practice in itself. It's not only my favorite language, but it's the one you should use if you're trying to serve and manipulate data on the front end in your HubSpot CMS. So the reason is you're able to pre render that code and cache that code in a way that takes a little bit of the load off the API and can help everything run a little smoother. And it's typically better than trying to pull it all in with just JavaScript. I guess I could like share this in slideshow. So the three basic pieces that I want to talk about are macros, callers and functions. So macros are kind of the smallest piece here and that's just going to be something reusable, very similar to a function in other languages. Callers are kind of that next step up where you can have macros within macros by having these callers that allow you to add some more complexity to those code structures. And then functions are the super fun part. That's where you can pull in all of your object data, all of your Hub DB data, contact information for personalization. So, so that's, that's a really critical piece as well. So macros, I use them all the time, especially if it's something that needs to be repeated. As a general rule, I do not want to do things more than once. And so usually if, if I see that coming, I'll just make a macro and put it in my macros file and just kind of use it from there. So this code here is an example of just a simple search input that allows you to add a placeholder and button text so that you can pass those values into your macro and display them dynamically. So going a little deeper than that, let's look at an example of callers. So a caller is the function itself is written like a macro, but when you use it in your code, you're going to call the macro name, pass in your values, and then anything that is rendered here will, will also kind of fall into that structure. So here we've got a render filter group macro that's called, so that I can wrap the actual filter options within that group. Looking at some of the functions that you can use to get data, the ones that are highlighted here in orange obviously are my favorite. I use them all the time. But as Ashley kind of alluded to at the beginning, there is a new function out there for Hubble which is pretty cool, and it's for its color contrast. So it's going to be really useful for making sure that the colors that you're using in your theme are meeting those accessibility standards and have enough contrast to be seen even by people who are colorblind and things like that. It the function itself allows you to use a hex code or the RGB color code in order to make that happen. And so this is going to really enable you to do some logic around if these two colors don't have enough contrast, use a different combination. So using or it's going to make it a lot easier to manipulate those global colors in your theme and then have them automatically populate in a way.
That.
Makes more sense if you've ever had your light and dark colors kind of mix up when you change the colors over. That's generally what we're trying to avoid here. So super helpful new function. I will definitely be playing with it a lot more to, to update my themes with, with this. So, so to kind of explain all of this, I want to talk about my favorite, one of my favorite modules at least, which is a dynamic listing that has a search, sort and filter that the client or the marketer or whoever's using the module can, can actually update and manipulate in ways that are globally applied. And you don't. So you don't have to do it multiple times across pages. The things you want to Filter by are just always there for the filters. So we'll kind of look at how that works. I have a global data module that allows you to choose which object you want in your listing and then define functions for it. So here you'll see I have, I'm just going to use an events custom object for events for this example. And my functions are data, filter, search and sort. So for any one of these functions there are properties that you can add. In a repeater you can show as many different properties as you want. People often want to search by the name and the description or be able to filter by several different dropdowns. You can add those as many as you want here in this global module. Then on the page itself you can activate all the things and there's not a whole lot of other set up to it. So this has got some built in pagination and different things. So the pieces of the puzzle here, obviously my global data module that we looked at, but I'm going to export that to the template context. If you've never used that feature, essentially what it allows you to do is instead of rendering that module on a page, it makes those variables that you set in the module available at the template level. And so we'll see why that's important in a second. The other part is my dynamic listing module, which at the top includes these two files. So this is kind of the structure that I always use for these files. And macros represent anything that is used to print out either HTML, JavaScript or CSS. And then the config file is just pure Hubble. So the config file is more for establishing variables that you want to use throughout the module itself. So those can be imported. Here I'm importing the entire file because I think there's only one macro in there. And then we've got the macros file here. So I'm selectively pulling in the ones that I want. And so the, the final result is that you have your, your global options for the filters and the search events, enabling this front end experience in a, in a really flexible way. And so let's look at the code. So I'm also pulling in just some like contact data first name just to add some personalization. And then we get into the module here. So the object ID is events. That's how it knows which CRM function to, or which Hubble function to use. And then I've got my search and filter toggled on. So to remind you how it knows which things are searchable and which things are filterable that is stored in this global data module. So if I click into events, I'll see that I have functions to grab data, all the different properties that I want. The filter shows the topic, so it's automatically based on this property name. Pulling in all of the options for you and then for the search allows you to gives you some flexibility so they can search through essentially anything. So as far as. So the. The final result we've kind of seen. Let's look at some of the code.
So.
So here is my listing module. So this should all look familiar. This is the main caller that I'm using and, and I'm pulling in that global data, passing in the object type and then all of the data that I have and that is creating everything else you see here. So getting to the filter props happens in the config file. Now we get to the real fun part. So in your config file you can have. So this is where I'm processing all of the global data module. And so to get these filter properties, this is just another best practice here. If you're trying to drill down into an array, the best way to do that without having to like loop through everything and pass it out of your loop or something like that, you can use these filters like select attribute, reject attribute and then just kind of concatenate them to navigate your way to the actual property that you want. So this is. All of this is getting the repeater for properties that we saw in the global data module for the filter action. So that's kind of the best way to manipulate that. That data. And then the rest of this kind of just goes through and establishes what the module needs to be able to loop through the right things and create those front end objects. My macros file, like I said, is more, more for rendering the cards. This is where I'm establishing the classes that are used in my isotope function. That's typically what I use for search and sorts and things like that. So it's all kind of based around that system and it's also rendering HTML. So that's kind of how I make that delineation between the two files so it's easier to navigate around. The last bit that I wanted to show you is always as a best practice, if you, if you need to use one of these CRM objects or hub DB tables, be super careful about using them in a loop. You're much better off getting them all at the top of the page. Just pull all the data that you can at the very top of the page so that you don't accidentally go past 10 loops and then all of a sudden your data is dropping out and you're not able to render anything. The best way to do that is define things at the top and then refine them further in your code and use these concatenated filters to pull out what you need. Yeah, and I think that about covers it for me. Hopefully I didn't go super over 10 minutes, but if I would love for you to connect with me on LinkedIn, talk to me on the developer Slack, hit me up in the community. This is my favorite thing to talk about, so I'm always here to chat. Oh, are you muted or am I hearing. Not hearing things.
It.
Oh, my mic was off. Sorry for that. This was incredible. Thanks, Aaron. And I want to talk about atomic design and how you can use it, because if you work on big themes and a lot of files, structuring is key because it enables you to work faster and save a lot of time. So for those of you who are not familiar with atomic design, it's a mythology by Brad Frost. And if you dig into it from my experience, you can apply it to almost every aspect of your work, of your life, however you want to work with that. So let's take a quick look on what or how atomic design is built. So basically it's that simple. So you have atoms combined. Atoms are molecules and many molecules results in organisms. And from organisms you go to templates and, and like everywhere you build pages upon templates. So how can you imagine an atom? So for that I have prepared a theme structure I'm working with always. So my folder structure looks like this and it's a bit different than the boilerplate that we all know, because from my experience, it's way easier to work with something like this in terms of folder structuring a theme. And if we dive into the folders, you see I have a variable file where I put all the variables from the theme or from the fields JSON or even set separate ones that I am using throughout all of my modules and CSS files. For instance, I have color object.
And.
I'm putting all the colors from the fields into one object and if I need them, I'm. I am pulling them just like this. So don't worry, this is just a macro I have created for in this case colors. I have the same for spacings and alignments. Like those are my helper classes.
And.
Yeah like my recommendation is always to separate it as far as possible. So you work on smaller files because it's if changes need needed to be done quickly, you don't want to look into a file with let's say several hundred or even thousand lines of code. So this is something I am using I can recommend. And as for macros, because Aaron showed us a great example. I'm using macros in this way and like I've mentioned, it's an atomic design approach. So for instance, I have a macro for a simple button icon where I get just the icon name, size and position. And for instance I'm passing this macro to the button macro like this.
And.
If we take a look at the module, like the button module itself, it looks a bit like this. So here's the layout by itself and what I also recommend and also do is if you're working on a theme, just create a separate CSS file which will be pulled into the module based on if there's a custom class provided or not. So for instance, if we have input field where the user can type in custom class, it will be also loading this separate CSS file so the user or the developer of the user won't mess up the default code. And the same is or the same goes also for JavaScripts, because you don't want to load all of your Javascripts by default into a page, you can work with booleans or select options based on them, load or download JS files.
And.
Yeah, like this is again a folder structure because I like to separate it into files as small as possible and work from there. So if you have questions, drop them in the chat and we will be uploading this folder structure to the GitHub account later so you can take a deep dive into the structure if you're interested. And yeah, I think John will take the stage.
I want to interrupt you real quickly, John, because apparently you decided that muting your mic was the best thing to do. So I'm going to give you another chance to start from scratch, collect yourself, point at the screen and go.
It seemed like the in thing to do. Sorry y'. All. Thank you. Anton, great stuff in your talk. I love how you implemented Atomic design using Hubble. It's something I've done myself. Super, super awesome. I'm John McLaren, senior developer advocate at HubSpot, and I'm going to teach you something new about making your site faster, fast, and how to collect some hacker tiers. Whoops, I just spoiled the. The. The teaser there. But before we get into that, when did you last run a speed test on your website? Please answer in the chat I'm watching. I'm only going to wait a few more seconds. You never ran a speed test, Dennis. You're fired. All right, well, seems like folks are being quiet. Cool. Cool. When I built it four years ago. So true. Well, March 12th, Google kind of replaced one of the core web vitals with a whole new one. Core web vitals are Google's metric based way of determining if a website feels fast. There's only three core web vitals and they're replacing one third of them. Core web vitals are what you see when you're on a Google Page Speed Insight report or use the Chrome dev tools to assess your speed and core web vitals are a Google search ranking factor. I'm not passes and.
Yeah, like my recommendation is always to separate it as far as possible. So you work on smaller files because if changes needed to be done quickly, you don't want to look into a file with let's say several hundred or even thousand lines of code. So this is something I am using I can recommend. And as for macros, because Aaron showed us a great example, I'm using macros in this way and like I've mentioned, it's an atomic design approach. So for instance I have a macro for a simple button icon where I get just the name, size and position. And for instance I'm passing this macro to the button macro like this.
And.
If we take a look at the module, like the button module itself, it looks a bit like this. So here's the layout by itself and what I also recommend and also do is if you're working on a theme, just create a separate CSS file which will be pulled into the module based on if there's a custom class provided or not. So for instance if we have input field where the user can type in custom class, it will be also loading this separate CSS file so the user or the developer of the user won't mess up the default code. And the same is or the same goes also for JavaScripts because you don't want to load all of your JavaScripts by default into a page. You can work with booleans or select options based on them. Load or download JS files and yeah like this is again a folder structure because I like to separate it into files as small as possible and work from there. So if you have questions, drop them in the chat and we will be uploading this folder structure to the GitHub account later so you can take a deep dive into the structure if you're interested. And yeah, I think John will take the stage.
I want to interrupt you real quickly, John, because apparently you decided that muting your mic was the best thing to do. So I'm going to give you another chance to start from scratch, collect yourself, point at the screen and go.
It seemed like the end thing to do. Sorry y'. All. Thank you. Anton, great stuff in your talk. I love how you implemented Atomic design using Hubble. It's something I've done myself. Super, super awesome. I'm John McLaren, senior developer advocate at HubSpot, and I'm going to teach you something new about making your site fast and how to collect some hacker tiers. Oops, I just spoiled the teaser there. But before we get into that, when did you last run a speed test on your website? Please answer in the chat watching. I'm only going to wait a few more seconds. Never. You never ran a speed test, Dennis, you're fired. All right, well, seems like folks are being quiet. Cool. Cool. When I built it four years ago. So true. So true. Well, March 12th Google kind of replaced one of the core web vitals with a whole new one. Core web vitals are Google's metric based way of determining if a website feels fast. There's only three core web vitals and they're replacing one third of them. Core web vitals are what you see when you run a Google Page Speed Insight report or use the Chrome dev tools to assess your speed and core web vitals are a Google search ranking factor. I'm not panicking though, here's what's happening. First input delay is going away and FID measured how long it took for your site to be able to respond to the user's first input. If the user click clicked a button right after loading the page and your page didn't handle that click for seconds after it happened, that's slow. If your site feels sluggish, users might leave and never come back. So that's what FID was used for. Google replaced FID first Input delay with interaction to next Paint. And while first first input delay was all about that first interaction, Interaction in nextpane measures the time it takes for your site to respond to the user's interactions. Like clicking a button, hovering over an element, etc. Across the board for the entire duration of the Visit. Interaction in XPaint is all about feeling fast. It's rendering changes quickly after the user interacts. If you didn't know what paint means, it's browser speak for the literal rendering of the pixels that make up your site. So what do you need to do to solve for inp? First, you should know that everything you already did for FID still totally useful. Keep it. It's not a problem. But some other things that you should take into account are instead of injecting large chunks of HTML, you should have that HTML already in the page and show or hide it based on the interaction. Obviously you can inject changes that need to happen to that data, but it's more surgical and easy for the browser to handle quickly compared to completely creating all of the elements from scratch and then rendering them. And then also in response to user interactions, apply a CSS class instead of say, applying a ton of individual styles to a style attribute in JavaScript. And then you also want to include width and height attributes for images, videos, iframes, and specifically in the situation of when those things will appear in response to a user's interaction. This will result in faster browser layout calculations and it'll just make your site feel a whole lot snappier. And then remember the goal for INP is the response to the user's interaction and making it feel like the page is reacting to that interaction as fast as possible. So in your JavaScript you'll want to. Oops, went a little ahead again. So in your JavaScript you'll want to simp. Oh, never mind, I was right. So in your JavaScript you want to simplify event listener callbacks, do less in them whenever possible and separate the critical and non critical responses to user interactions. Digging into that a little more, this is an example of a form submission where we need to update some UI on the page as well as save the data, which in our case will mean that we're going to submit it to a serverless function on HubSpot. This is a lot of tasks for the browser to have to do and and it has to do them in order. And then the save data function might take a little while because it's got to hit a serverless function. The function's got to process that data and come back. So there's a few things that we can do to make the experience feel faster. We can show a loading state immediately on the button after submission so the user sees right away that it's processed processing. We can validate the form and submit it, and the rest of the stuff is dependent on the form submission, so that we know is a lower priority. We can use Request, Animation, Frame and settimeout, which will basically tell the browser to treat this stuff as a separate task so that it can go do other things and come back. Then we update some hidden HTML on a modal display, the modal log our analytics and set the loading state to false, meaning the button's no longer showing that it's processing. This makes it feel a lot more responsive and we still have everything that we need to get done gets done. By the way, this settimeout and requestanimationframe thing feels a little bit like a hack, but it is the recommended way of actually doing this. There is another possible way you can use, which is basically you can use an API for task scheduling. I'm not getting into that just because that's a little advanced and you could accidentally make things worse. This is easier. This is simple. Go for it. Yeah, so I've talked about website speed in prior talks and we have a great guide for that in the docs. So I'm not going to restate some of the things that I've already talked about or shown in those docs. Links will be in the chat to these resources. Let's now talk about hacker tiers. I like to think that every time you make it harder for a bad actor to do something malicious, you're collecting some hacker tears. I want to help you collect some hacker tiers. So we're going to talk a little bit about security. When it comes to security, HubSpot does a lot for you in a layered fashion. So HubSpot's got a CDN with DDoS mitigation web application firewall, free SL with TLS 1.2 and up. We support two factor authentication for users. All user activity is logged so it can be reviewed later. The CMS itself, if you didn't know this is also designed to make it harder to accidentally leak data or introduce performance issues that could be exploited to slow down your site for other users. An example of the preventing accidental data leaks is we actually prevent you from displaying data like contacts, companies, deals, tickets and quotes on pages that are not set to password protected or membership pages. And then last, we have a 24. 7 security team that's monitoring for malware suspicious activity and they're constantly making updates to make your account and site more secure. And while HubSpot does a lot to help you avoid security blunders, you are still one of the layers of defense. If you're being careless putting your private app tokens in the front end of your sites, or committing them to a git repo, or sharing your personal access key to folks on the developer slack or something like that, most importantly, you don't want to be that guy who leaks their secrets like tokens in a presentation on the Internet. No one likes that guy. Moving on. More specifically, here are some security best practices. Obviously use a high quality password. Don't reuse your passwords. Use two factor authentication. This one's a little less obvious. The only CMS specific feature that requires you to have super admin access is the ability to create and manage private apps. If a junior developer doesn't need to be the one that's actually creating the private apps, maybe don't give them super admin. Treat your personal access keys and your private app tokens like they are a password. Never give it to anyone. Even if they claim they're from HubSpot, even if their name is Dennis or AJ or John, don't give it to them. We don't need it. We can help you in other ways. And you also wouldn't put your password in in your front end code. So don't put your private app token there either. Take care to also not commit your secrets like private app tokens to get if that repository is public, you've now become that person no one wants to be friends with because you just shared all their secrets. GitHub specifically has a tool under Repository settings for storing your secrets, so you don't need to actually commit them to your code. Now another thing that's very good to know because I keep bringing up private apps. Don't add every private app scope under the sun to your private app. Use only the ones that are relevant to what your serverless functions or integrations need to actually do. If your site has some front end code that's going to hit that serverless function, consider splitting into multiple private apps if you have those kinds of separate jobs to be done is how I think of it. So one example is like you might have a serverless function that's grabbing some HubDB data. That same function does not need the ability to write the contacts, but you might have another serverless function that needs that ability. So if you separate that into two separate private apps, you've now made it more secure. And then last, you don't want to trust user submitted data. An example is you have a form that that submits to a serverless function which then stores that data in to say I'm just using HubDB as an example, but it could be anywhere and bad actors discover your form. Well what are they going to try and do? They're going to submit HTML, JavaScript and other garbage into that form that you don't want to display on your site. Say again, you want to display some data that's coming from a form like this on your site. You don't want to just print that data to the page. It could have HTML, Hubble, etc. In it. So what can you do? Hubble actually has a whole bunch of filters that you can use depending on what you're trying to do. For example, Escape HTML Curve converts all special HTML characters to their Unicode version. It'll visually show the HTML. This is an example that you see in all developer documentation. With HTML, you can actually read the characters instead of it actually rendering the elements. Hubble also has a strip tags filter which will allow you to just instead of displaying that HTML, just totally eliminate it from the text string. And if you're putting content into an HTML attribute, you can force the characters to be only ones allowed in HTML attributes using the Escape attribute filter. If you are worried that there could be Hubble or handlebars like JavaScript, handlebars like code in the data, you can also use Escape Ginjava. Little history for you. The reason it's Jinjava is Hubble is actually basically HubSpot's fork of Jinjava, which is also based on Jinja, a Python templating language anyhow. So similarly, similarly, similarly for JavaScript and JSON, there's also escape JS and escape JSON. And in situations where we are confident that the data you're trying to access should not contain Hubble, JavaScript or HTML, HubSpot actually automatically applies filtering just like this for you. But of course there are legitimate scenarios where you might want to say like in a HubDB value, store some HTML for formatting sake. So we can't just do that everywhere without possibly breaking some use cases that you might have. In addition, there's another way to protect against cross site scripting and other malicious traffic available in all tiers of HubSpot. You have the ability in domain settings to configure a content security policy and set security headers. For most sites I Recommend Enabling require HTTPs all modern browsers support HTTPs, and for most sites I recommend also enabling TLS 1.2 plus. It's going to be fine as all modern browsers support it and then also in security headers. A way to think of it is the more of these top level features that you can enable, the better. In this Security headers tab, however, understand that you can bork your site if you're not careful because you are protecting your site and users by basically putting limits on what your website is allowed to do X frame options, for example, lets you control whether other sites can load your site in an iframe. If you need another site to embed iframes from your domain, this would totally break that Content Security Policy is the easiest header to accidentally get wrong. I've made this mistake a bunch of times myself, so my recommendation on this one is to go online and look for a There are generators that basically you can pass your website address into and it'll help you tailor it. The goal is you want to enable as many of these features as you can can safely without breaking your site. So after you've done that though, I wouldn't just copy and paste what it gives you into the Content Security Policy Directive field. Instead you actually should enable the Content Security Policy Report only field and paste it there. This will enable you to go onto your website and in the developer console. Just keep navigating around your site and you'll see all of the possible violations that your site currently has so that you can then either fix those violations or remove those from the security policy so that you don't have to worry about it breaking that part of your site. Links to our security resources will be in the chat. I share a lot of HubSpot development tips, tricks and news on my LinkedIn account. Feel free to follow there. Of course. As always, please subscribe to the Developer Changelog and Developer Newsletter. Thanks for listening. We're going to move on now into Q and A and it's not just Q and A on what I talked about, but all of the folks who who shared with you today are coming back on. So drop your questions in the chat. And I have a lot of. I had a lot of links. Dennis, I think linked them all up.
So I hope that I did link them out. John Great job everyone. Wonderful job. Proud of you all as per usual. I want to bring this over here so I'm looking at the camera. I'm also going to put this up on the screen right now because I want everyone to go ahead and register for our next event next month, which will be all about quotes using the custom quote templates, the Quotes API and we'll start kind of hinting at more Commerce Hub type stuff that's going to be coming out, which I'm very excited about to talk about. All right, we are over time, but that is okay. We have some questions. This is basically all for Martin because Martin asked all kinds of questions. If anyone else has questions, put them in the chat and I'll try to.
Find them.
This one is for Tune who is not here but we can answer it. When you are importing using the API to import your CMS template, how can you get the images, URLs and assets which will not be brought over? You can use the files API, correct? John, does that sound about right to you?
I don't know from where in the. In the in Tune stock this was from, so I can't.
Yeah, that's my assumption. Martin, we can talk about more in detail later on or we. I'll yell at Tune to send you a slack message. Moving on to. Yep. Martin, should we mic everything to Hubble to React? I think John, you did answer this but I wanted to put out here again because A I want to make sure everyone's aware that React is a thing that you can do in the CMS and I encourage you to try it out and B know you don't have to. There are benefits to either way. But John, if you have any more information on that, I'll allow you to talk now.
Glad I'm allowed. Yeah. So as Dennis said, you can build with React on HubSpot Specifically we let you build modules and template partial with React and the answer is no. You don't have to panic and be like oh no, I need to convert my entire theme from Hubble to React. That's not the case. Hubble is sticking around. React is another way of building and there is a different workflow. When you are building with React, it's using the same project system that we use for for UI components for UI extensions. So it's just your preference. Do you want to use those tools or do you want to keep doing what you're used to?
Indeed. And you can slowly integrate React into your stuff as you go moving along. And this one is for Aaron and you'll need context because it means nothing without context. Is that logged in component? Is this a logged in component only? This is when you were demoing the code for it, do you recall? I'm assuming yes, it is.
It is not, but can be. So let me dive into that a little more. The events object that I'm using is a custom object and so that can be pulled pulled into the the front end on the CMS pretty easily. I did pull in a contact property so when it said like welcome Aaron, typically that is going to be logged in only. However you can't because you can't pull contact data specifically using couple unless you use a GraphQL. So that's kind of the way around that. That's how that one in particular is not logged in only. Cool.
Guess who has another question. Oh, Martin, hi. How are you doing there? This means taking more logic to the front end versus the back end. Oh my gosh, I forgot the context of this one. So I am sorry I had to skip it because I don't remember what it was for. I'm going to move on to the next question which was yeah, Martin, is there a Hubble way to call an API from a private app or whatever? I don't think. Well go John, tell me because I.
Don'T know that if there is not from a private app but you can grab obviously as you mentioned, all the CRM objects, CRM associations, etc. As well as HubDB through Hubble and GraphQL. GraphQL is built into CMS and you can use that for templates and for modules. So you cannot however be like hey, I have this dad joke API from the US Government. That's a real thing. You cannot just go I want to hit that API through Hubble. If you want to do that. Best way to probably do that if there is an API key, use a serverless function.
Actually you know what, I'm going to skip a couple here because Byron had a question about serverless functions. How can we have more security security and serverless function endpoint?
This one I think Byron, if you are on the developer slack, I'd like to have a a chat with you and kind of dig into a little bit more. I feel like there's possibly context that I can't like I either won't know or won't be able to relay this way.
Yeah, I think this is probably one of those. It takes a lot more conversation to even really understand. One liner is not going to work for this one. Luke asked, what do you think about using webpack in local development environments for optimizing themes? Just did that and it seems to work like a charm. And you are correct. Luke, do it. I love it. As long as it doesn't get too convoluted. Right. Sometimes I feel like Webpack gets a little too crazy out there, but that's my opinion. Anyone else on this crew here have thoughts on Webpack and using it?
I mean, I agree it's definitely. It can get a little convoluted pretty quickly, especially if there's multiple people working on the same set of files and stuff. So definitely a good way to go. It's just something you want to be careful about how you implement that.
Totally agree. Back to Martin who asked A question I think this was actually answered. Yes, there is a boilerplate equipment equivalent for react. It's on GitHub and if it wasn't added to the chat, yell at me later on and I'll make sure to send something to you. Boilerplate might be the wrong word for it. It's an example use case of using React. Byron did that one and then Esther, this one goes out to Anton. Any performance concerns when going atomic? Creating many many HTML CSS files instead of having big one big one.
I wouldn't say concerns to be honest. From my experience it can have a performance benefit because if you're loading one big file, I mean this file can contain several hundred or thousand lines and going atomic can lead to a benefit where you load just the files that you really need.
So.
I don't have experienced negative impacts but this is something, something you would need to compare because it's yeah always depending on what you're working on.
Yeah, I, I'm, I'm 100 with you on this Anton. The only thing I can say is once again don't go too crazy because it can get convoluted and you get lost in your like knowing where things actually are. That's the only concern I ever have with this philosophy. I need to put this up there because it's important the US government runs dad jokes API. Yes indeed it does, which is pretty freaking amazing. Moving right along and this is gonna be the last question because we we're over but one asked about the performance improvement. Is there another tip for the standard includes like JS or CSS that HubSpot loads for you? Common question John. Tackle it.
Well. So first, if you're referring to how you load your own JavaScript or CSS, we have require functions that you should absolutely use. So it would be requirejs or requirecss that will make it so that we will conditionally load Your CSS or JavaScript based on where it's being used and what's whether it's actually going to be used. Cool tip. You can actually include that inside of if conditions so you can conditionally load JavaScript aside from just it being in a module and that module is added to a page.
Really love it. We're going to wrap it up right here. I put down the event details for the next hug. Again, if you have more questions we are all pretty darn active in the developer Slack so get over there, ask us questions and we will try our best to help you out. We'll be sending up a follow up email with a link to this video, some additional resources, because these. These folks did some great little coding snippets for you to look at. And that's all. You all have yourselves a great day, and we'll talk again soon.










