 Thanks for coming to the session today. Just as a bit of an overview of what we're talking about today. So myself and my colleague are from FlightCenter. And for starters, we're going to give a little bit of an overview of our digital transformation, but then dive a little bit more into a specific feature we've built for the site on AED Couple of Stack. So I've kind of already given you a bit of information about myself. My name is Andrew. I've worked for FlightCenter for about three years now. I've been a web developer for seven years and started with Dribble 6 and WordPress. I'm currently working with Drupal9 React in Next.js. I'll pass you over to Joanna and she'll tell you a little bit about herself. Thank you. I'm a little bit tiny, so I have to adjust the microphone, of course. So welcome everybody. Well, my name is Joanna. I have been working for FlightCenter for seven-ish years. I have been doing Drupal since Drupal 4.5, back to 9 already. So like 15-something years on doing a lot of Drupal stuff. And apart from that, a little bit more of React and Serverless. That is a very complicated word to pronounce. But let's dive into this thing. So let's talk about the previous state of how we were using Drupal at FlightCenter. So we started from Drupal 7, a multi-side environment that we had a base code base that a lot of teams were using. But slowly things started to drift apart. The same thing was said to be built in different and slightly ways that we love to do kind of reinvent the wheel over and over again. And apart from that, these also laid that differences of way of building things. We have SAS there, a little bit of React, HTML Lord, neither, both. And also that led us to not being able to share the content easily when we create futures or capabilities. It was so hard to roll it over. And something that is very visual. We had brand inconsistencies within the same brand across multiple regions or countries. So why we made that change, you might be wondering. So we have a lot of teams, small teams in different countries that were all working kind of their way. It was as well when we wanted to create a new feature or enhancement or something, it was very expensive that not talking about money. But you guys know that time is money in the end and it also takes time to build things. And when you want to be building things and exciting new things, it was hard to roll out to other countries. And also that means that you couldn't provide that solution to all the regions because of budget things. And one tiny little microscopic pandemic item on the list as well was the major thing that made us lead us to make this change. And we kind of yolo it in the end. Why not? Now that we have given a tiny, big opportunity, let's do it. That's what we said. So the big wins of yoloing it, all of these. For everybody around the globe that this approach is not only for Australia, we were able to provide solutions or capabilities for future faster that we could be bought. We were able to have a way of having a design system, a consistency between our brands. We were able to reach out those regions that we couldn't do it before. And we were able to provide the same content for whoever region wanted to use it. And that also led us to build things faster and do AB testing as well in a way that instead of waiting for the whole idea or requirement to get finalized, just iterate on it, testing it here and there, what works, what doesn't work and be agile and changing little by little. And those kind of a little bit of scratching the surface, the big wins that we have seen that about doing all of these decoupling and CMS solution. So I'm going to pass it back to Andrew. So he's going to start talking a little bit more technical in the more technical approach of how we ended up doing all of this. Okay, so what we have up here is just a fairly simple architecture overview. So we have a decoupled Drupal site with Next.js on the front end. We've always wanted to do this sort of approach, but we've never had enough reason to do it. I think Josh from Acria, if you're in here, you gave a fantastic talk previous to this session on some of the reasons why you wouldn't want to do it and some of the negative effects that might have. So we were very lucky in that the globalization force in the business really opened us up to be able to work on something like this. So just starting from the bottom, you can see each of our domains. So these are the domains for the different regions we have. Previously, they were all individual sites and still partially are, but we use CloudFront in between our domains and the Next.js application. So we proxy all our requests to Next.js. Each of these individual domains have a rule set assigned to each of them. We have a CloudFront distribution where we manage those rules. And because of that, we're able to pass things like the locale and brand down to Next.js as context. So that really lets us have a multilingual, multi-brand Drupal site, a decoupled Drupal site. So once a user gets into Next.js, we use those details that we passed down from CloudFront to make requests back to Drupal and fetch data or nodes, data for nodes via JSON API. If you jump back up into that corner there with the Drupal logo, we've got most of the work we do, just content nodes with an alias assigned to them. So when we make that request, we do that with the locale, the brand and the alias that we want. So if you look at that Drupal box, that content type, it's usually just a content type with a layout builder field assigned to it. We also have a menu service in our Drupal 9 site. So that supplies menu links for our shared header and footers across each of those sites. And we also feed in app config from AWS. So that's primarily just brand information or brand specific data that we want to keep outside of Drupal in a global space for other apps to use. So we're feeding all this information into Next.js. And if you look at the box with the React logo, so we're feeding in a lot of React components to Next.js. And we have a lot of other internal teams across a large number of different languages, from .NET to Java to PHP. The one commonality between them all is they use React. So we're able to pull in those React components from all of those other teams. And all of the pages in our Next.js application are primarily top of funnel pages. So as a user goes through that funnel, they'll eventually get directed off into things like secure booking portals. And using React components, we're able to create that seamless experience through things like a shared header and footer. So we want to talk today about one particular feature. So we got this request to come down where we needed a multilingual, multi-brand Drupal site, which you're thinking its owner is fairly complicated or complicated enough. But then we had an additional request from Marketing who wanted a page-building solution for all of their campaign and deal pages. There were some key challenges in this. These four key challenges sort of dictate the rest of the presentation, so we go a little bit more deep into them. But things we needed to solve was we needed props for those components to come from block config itself to be able to use those blocks in something like Layout Builder. We needed to map those blocks to React components. Another big one, we needed to pass layout information down to Next.js. And the final, one of the trickier solutions was we needed to have a preview solution for those editors as they are building out those pages. So I might pass back over to Joanna who's going to talk to the block config we've done in a little bit how we've set those up to be able to accept those props and pass them down. Well, now we get into the dribble part in the beginning. So for us to build a whole page, we have to start by this small element. We use dribble blocks. So to do that, and what Andrew just talked about, the end-to-end solution, we had some requirements. So we needed to pass the content within the props to the React component. We needed that functionality to push back to the editor that is looking at the page in dribble so it can reflect in real time what he is doing. Is that a title? He wants to see the title being displayed as well. The blocks needed to work with the layer builder as we decided to use layer builder to provide the functionality of editors to create the whole page how they wanted to do it. And another part that we decided to use JSON API also as our API service to provide that connection between the backend and the front-end, which is the dribble part and the React part. So we needed to ensure that those props with that content was exposed in a way that we could consume it from the React end. And apart from that, that wasn't enough, we needed to make sure that each component that was placed in the JSON API, we need to find a way to grab it as well in React and say to be able to identify it in a unique way to be able to render where it has to be displayed at this place, at this order on the page. Now that we have the technical requirements, let's start seeing how we did it. So on the, I was going to say on the black box, but both are black. The white one that you guys see, it's the visual representation of a block plugin form that you can see. Let's start using the simple component that we have, the text component as a title, a title, and with a week. So we created that block. We presented this to the form, but the juicy part of this guy here is that when we have the block submit method of that class function, you guys can see that we are creating an array where the key of that array is the name of the probe that we are using in the React component and the value is the value that the user entered. And in the end, we are JSON encoding that variable into a single field to be able to send it through the JSON API. So all of these will look like something like that in the output. On the green part this time is how the JSON API will look like for that text component. We highlighted what is the block plugin ID, which in this case amazingly is as simple as text. And you guys can also see the component configuration attribute values that has been JSON encoded that array that you guys previously saw. And this is how we start doing the magic here. And on the right side of the screen, I mean, yeah, on the right side of the screen, you guys can see as an example, the block box is the React component, which is saying that I'm using the text React component and I'm spreading the props from the component configuration and the array will take you down to say, and this is what I'm rendering. One good thing of this diagram is that the props that the component is rendering is controlled from Drupal. It has no extra steps or mapping or logic in between when it gets to the React end. This means that any enhancement, changes, additions, deletions happens at the Drupal end because if you guys think about this, once the component, the block is placed in Liabroader and it gets added to the JSON output, it belongs to that node. It's attached to that node in the database, so you can't change it. So if any enhancements are changed, it gets reflected on the API service that we're going to use in React or in another services or applications that you might need in the future as well. So that key future was, it's very flexible in the end. Now that we know how we're sending, how are we sending the data from Drupal using blocks? Andrew is going to talk about from the other side of not the moon, the Drupal end, the whole development process and how are we receiving those props and how are we managing all of that in the next years? All right, so for the next few slides, I'm going to try and speed it up a little bit because I think we're getting a bit longer than what we expected on some of these slides. But yes, I want to talk about how we actually make that connection between the two. So how we get the Drupal, the JSON API data to map to a particular component. So we wanted to come up with a fairly simple, flexible solution that wasn't too overly complex. With a few requirements, we need to support Next.js dynamic imports so we get proper tree shaking and we're not importing unneeded code. We should map on a one-to-one basis for Drupal Block 2 component and we can also add some additional stuff in there to help us run things like integration tests on each of these components. So just looking at the diagram on the right-hand side, you'll see this mapping package is named Mappy Bird. We have a bit of a history app Flight Center for coming up with odd names and we love it so we try and stick to it. But you can see on the left underneath, you've got the Components directory and then you've got individual directories for each of those components. If you break into one of those component directories, we've got an index.js and the mock data.js file. So that's what I was talking about when we have a bit of extra information in there. I think that's actually incorrect because I do believe that's a JSON file. But it just contains some extra information we can use or default props or mock props to be able to mount those components for testing without having any data coming from Drupal. On the right-hand side is the actual map with the index.js. So this is really just a JavaScript object that we can use for dynamic component imports. And these slides go a little bit more into detail than how that's set up. So on the right-hand side, you can see the component structure there. There's one key thing we need to work around for this and that is Next.js Dynamic only supports default exports. That may be changing in future versions or already has. But when we implemented this, we only can use components with default exports. We're done in those component folders and the index.js, which you can see on the left, that screenshot, we're just importing components from other packages and then re-exporting them using a default export. If I jump to the next slide, this is the actual index.js for the map itself. So this is where we reference those components and we use Next.js Dynamic to actually import them. And like I said, it's just a JavaScript object with those imports. So we can reference those in that object. One key thing to note is the keys of this object. So those names you can see with accordion, button, meter, gallery, text. Those are actually Drupal block plug-in IDs. And that's what we use to map the Drupal blocks on. One other benefit of using a map like this is you don't have to just store dynamic imports in here. You can add additional data or, like I mentioned earlier, things like mock data. But also because we're using Next.js Dynamic in here, we can actually do, we can apply specific settings or dynamic import settings to those. So things like disabling SSR and components on a per-component basis. And just a quick example of how this map might be used. So here you can see we're importing our component map and then we've just got a test component there that will accept a block plug-in ID or that component prop and then the actual props, which might also come from Drupal. But you can see here we're using the component map. We're referencing that ID and grabbing the component out of that map. And then finally just passing those props over that component below. Cool, so that's how we would map components to Drupal blocks. There's still one missing part to that as well, though, is how we actually tie all that together in Next.js. So this might help a little bit, understand how that happens. And for the start of this, I'm going to pass back to Joanna who's going to talk about this from a Drupal perspective. So now that we have the page element, let's go to the whole page to finish it. So do you guys have heard about Layer Builder? Hopefully you guys have. And what we did was we found the Layer Builder, the Bootstrap contract module. As you guys can see, that's why this screen shot looks very familiar because we were inspired by that UI that we loved. I mean, I liked it. But that's the only thing that it might look familiar to it. The rest of it, how it works underneath, is entirely different. For this type of, for our decouple solution, we're doing exactly, not exactly, similar to what we did with block plugins. But the difference is that we're passing not classes, we're passing MUI props because we're rendering our components using a material UI. So on the right side, you guys can see that we're passing the section itself, we're passing like the max with the sludge because that's MUI and any styles or elements or each column might be whatever material you need to do. So all of these will help us to save time the same way that we did for blocks. And for the rendering part, we built our counterpart Layer Builder React solution. But in this case, it's using the grid up from material UI. So, and at the same way, it's getting the props from the JSON and spreading the props for the grid container or for the grid item for each column. And as Andrew just explained in line 86, you guys can see it's reading the component prop, which is the plugin ID. And then in line 91, it's rendering the component as well. This is the larger picture of the Layer Builder mapping from the drippling till the React end. And now that we have created the page, let's go to the final part about previewing this whole page before it gets published. We need to find a solution to give editors the options to say, hey, what a build? Does it look okay? We're in the trap mode many times and we have to have a visual representation of it before it gets published. And Andrew is going to take you way deeper into this one. Cool. So after page preview, I might quickly jump back to that slide to talk about something additional. Sorry, Joanna. So one key call out for this component is the way we spread the props across the material UI grid components. So Joanna was talking about the structure that we're outputting here from Drupal from the Layer Builder settings. So these, like she mentioned, are all material UI props. But looking at how we're spreading these across those material UI grid components, that's very intentional. So by spreading those props, we can then pass any material UI props that are compatible with those components from Drupal. What that lets us do is we can compose complete layouts in Drupal or make updates in Drupal, but never have to touch this layout component or change anything about it because it's still going to be spreading those props across those components. Yeah, so page preview. So we have a way of rendering layouts and components in Next.js from Drupal via fetching on the alias of those pages. That's all done in Next.js and get-serverside-props-serverside. We had another problem. We got to this point and marketers said, hey, we need to be able to see what we're actually building. And having a 10-minute cache on the front end from published nodes was getting a bit painful. And there was no true way to see what they were doing in Drupal for things like drafts and revisions. So when we're fetching from JSON API, we don't have any authorization on that endpoint. It's purely just public or any data associated with that anonymous role in Drupal. There are a few security concerns there, so there wasn't a lot we could do in regards to that because it is only just serving up an anonymous data or anything associated with that anonymous role. However, we did take a few extra steps to conceal things like module names or user IDs or anything that could be used to gain traction and attack from an endpoint. But we're at the stage now where we need to get these components back into Drupal. And we've already done this in the past with some of our legacy stuff on Drupal 7. So when we were looking at Drupal 7 End of Life, we made a big investment into React and React components to try and get a bit of separation from that Drupal backend, and we were mounting those components inside of Drupal. So when it came to that time upgrading to Drupal 8, we already had a lot of those content components. There was a pivot to this decoupled approach where we were able to reuse those React components. So this method of getting them back into Drupal is very much what we've done in the past. So we were kind of lucky we already sort of had a solution there and an approach to this. We did have two options, however, that we investigated. The first being a true preview solution in Next.js on the front end. So the challenges with this was doing it in a secure way. So the solution we were looking at was generating JSON web tokens for each user and storing those in our SSO provider opter. They have a really useful feature where you can store key value pairs in opter. So then we could use those when fetching content or making requests to the Drupal endpoint from the Next.js site. And they would honor all of those. That particular uses permissions and roles when making that request. But there were some drawbacks. So this did require quite a lot of work. We didn't have time to do. And there was more security risk here. One of the key factors that made us not go with this option was we couldn't guarantee, like, 100% secure method of doing this at the time. So we went with the second option, which is components mounted within Drupal using our existing component map built and spoke about earlier. This would let us mount individual components on our layout edit page. So you could really get, like, a true preview of what you're adding on that layout edit page. And then we were looking at mounting our whole layout component that we just spoke about on our NodeView pages. The problem with this, so it's kind of difficult to get a true page preview if those components don't live in the component map. So remembering back to what we spoke about earlier, we have a lot of components coming into Next.js to compose a page. Not all of those are coming from Drupal or bed Drupal content. So this was really only a good solution for that page editor solution where 99% of that page was coming from Drupal and in that component map. So it was just something we needed to keep in mind. One of the major benefits of this as well kind of fixed the solution we had in the past with our React and Drupal solution. So we ran into a lot of style bleed. So a lot of base theme CSS would affect our components. You might have seen, like, you get those base-contrib themes that white-style just an 80 tag or just a paragraph. So that was sort of bleeding into our components. Doing it this way, we needed no CSS or no SAS or no styling coming from the Drupal theme itself. All of that would be stored within the component using JSS and via our MIUI theme provider. We could also switch between our theme providers depending on the locale or brand which was also an added benefit there. So we didn't have to worry about CSS in Drupal. Cool, so this is the component map we spoke about earlier. Bad news is this is not going to work for this solution, so we need to make some upgrades. One key thing to take note is that React Lazy Loading is not supported by Next.js, well, it kind of is, but it uses dynamic loading which is really just a wrapper for Next.js to provide some of that SSR capability. So we had a problem. This component map is heavily tied to Next.js. How do we make it work with React Lazy Loading in Drupal? There was also a risk of different component versions between our front-end solution and our back-end solution. We want to make sure that what you're viewing in Drupal is what's going to show on the front-end. So this is our new component map, our component map upgrades. This is actually two maps, not one map. So if you look on the right-hand side you've got the dynamic map which is used in Next.js and then on the left-hand side you've got the lazy loading or the React Lazy-based map that we use in Drupal. The key thing to call out here is that they still share that one component's directory. So we're never going to have a difference in versions on our packages or components. The additional thing we needed to add to our React Lazy-map was an entry point for Drupal. So something to read through the DOM, find those React root elements and mount the components too. The benefit of this as well to some other solutions we've seen is that it's really only one JavaScript bundle thanks to React Lazy-loading that we need to add into Drupal which results in just a single Drupal library that we can add to wherever that is needed in pre-process functions. Cool, so we have this component map that's going to work for us but we need to go back to our Drupal blocks and make a few changes to support this component map. So this is actually the build method of our block plugins. This is not our individual block plugins but a parent block plugin. So this build method applies to all of our content blocks. And what you can see on line 10 is we have a component info array. So this is just component-specific information. So things like the plugin ID, the theme that it's using or the material UI theme and the locale. And then this is added in the markup that we output as a data attribute. And then we have a second data attribute which contains the JSON encoded field that you saw earlier with the block config coming out of that block. So then that allowed us to build the entry point for Drupal for our component map in Drupal. So going into a little bit more detail of getting a little bit more technical. So if you have a look on the right-hand side we have a bootstrap component using suspense loading so we get that loading state. And we add in a few different context providers here. So we do add a few more than this but I've tried to simplify it down a little bit for the purpose of this talk. So we have an emotion cache provider for our material UI styles. We add our theme provider like I said before we have suspense and then we have the mapped component which you can see right at the top there where we're using that component map to pull that out from. This bootstrap component is then used in our endpoint sorry, our entry point which you can see on the left-hand side. And what you can see this doing is fetching or searching through the DOM for those particular data attributes and pulling the information out of them and then using that information to mount that component on that React root element or on that markup that the block is outputting in its build method. We've also got if you look right at the bottom there there's some additional code to help in refreshing these components so when you think about Layout Builder when you make a change to that block config Drupal is going to perform an Ajax call and return the updated markup for that in Layout Builder when that happens we need to trigger a refresh and remount that component we did run into some trouble here if anyone's dug under the hood in Layout Builder you know there's quite a lot of Ajax requests going on and finding the right Ajax request to perform this refresh can be pretty tricky let alone doing it on a per component basis so it's a real simple solution to this when that component mounts on the the React root element we just add an additional data attribute just to say it has rendered then what happens when you update that component the Ajax call happens in Drupal that markup gets changed and that data attribute is no longer there what that lets us do is it makes it really easy from a Drupal perspective just to run this on every Layout Builder Ajax call and we know it's only ever going to happen or remount those components that have updated or changed which resulted in like a fairly quick update process or refresh process from Drupal so I'm going to pass over to Joanne who's going to do a bit of a live demo of one of these pages of Layout Builder in use here is a patch an admin patch the only thing that you guys can see that is Drupal scrolling down it's not even the footer it's not even the header only the admin menu and the menu items everything else is rendered through React so and to make things again like I like to call it the juicy stuff is that this is the Layout Builder you guys can see there is a section here there is a component here and for example you configure the section that you guys have known the Layout Builder but if you want to check this is how you configure the text component the first screenshot you have it here over there and when you render you can change the section width to have one small container or the big container and the content is adjusting it's hard to see here because it doesn't bump quite easily but probably if I just use the image you guys might see that the image might shrink a little bit and this is how exactly what the editors might be seeing when I just add a dummy text it gets rendered straight away in the component itself and if I just show you a little bit more about inspecting the code you guys will see that everything that is rendered there is material UI previous solution thank you very much just one more thing we did cheat a little bit with that Layout Builder so the way those widths change on the page are actually CSS classes from Drupal from that component that the Layout Builder adds so those widths aren't the preview of those is actually CSS coming from Drupal not the front end but all of those components are individually mounted in each of those sections and then just quickly looking at a node view page so this is the single Layout Component where we render an entire page or that Layout Component we showed a little bit earlier and this is the same that we use on the front end so you get that sort of preview, that one-to-one preview all on the same component, same version but yes I just wanted to thank you guys for coming this was our first Drupal South presentation hopefully we did okay if you do have any questions you can find us on the Drupal Slack workspace on the Australia NZ channel there we go oh we've got a slide for that so yeah feel free to shoot us an email, find us on Slack or come see us in the break we could talk about this all day and open to any other thoughts about this as well thank you