Integrating Posthog (@posthog) with Weweb (@weweb_io)

How to add PostHog analytics and feature flags to a WeWeb no-code site. Direct DOM manipulation fails because WeWeb re-renders, so instead bind button text to a WeWeb variable and update it from PostHog's callback using lib.variable.update_value. Use the community's ScriptTag tool to inject code without republishing each time.
Chapters
So this one's from the StageChange community. We're seeing a lot of interest in the course of the last couple months in telemetry, where people are taking their mobile apps, taking their web apps, whatever they might be, and they are adding telemetry systems like Nucleus or Heap or PostHog, which is what we're going to talk about today, and integrating it with these no-code front-end systems. Now, Most of these no-code frontend systems weren't designed with these things in mind as a first-class consideration. And in fact, the, the documentation for these systems usually doesn't talk about most of the no-code platforms per se. So we're going to work with this a little bit different. Here we have a tutorial on Webflow, which of course is a very popular no-code system for static sites, working with Post-op. We're going to try to apply it over to WeWeb, which is really about making more dynamic applications, but that lots of people use for their marketing sites as well. Let's talk about what PostHog does. PostHog is a telemetry and experiment management system for managing feature flags to decide who's where, to be understanding where your customers are in their journey, understanding how they are using your system. It's a very cool system. They have a pretty generous free tier and then some pay-as-you-go after that. As I alluded to in the beginning, they are not the only people who do this, but they're the ones we're asked about today. So let's see how we can make their integration happen and what lessons we get along the way that are going to be able to be applied to other platforms as well. So let's start with what the documentation has to say, which is first we need to set up PostHog itself. And I've already gone through the assignment procedure for PostHog. In order to get to where we are here, I needed to set up a— I needed to first sign up for PostHog. I needed to actually get on their paid plan in order to, in order to use this., but their paid plan doesn't actually charge you. It's just a, it's a metered basis. So the first million or so requests are free, and then they start charging you after that, after you hit scale. So they required my credit card, but didn't require me to actually pay anything, at least not for the present moment. Here I have, you know, additional feature flags, and I created one that's called CTA based on the Webflow A/B tests thing that they, you know, had us set up in here. I also got the, and they give you this link when you're walking through it as well. But if I sort of move out of the way, I can go down to project settings down here. They give you a script tag that you can insert into your WeWeb. Under most circumstances, I would at this point start talking about using the state change WeWeb embed tool that we've made as a free gift to the community that you can use. But it's actually not very good for this particular job. And that's because PostHog and probably some of these other telemetry systems too are deeply embedded into how the window and document work inside the browser. That's why they're able to pick up all of these automatic things like the page transitions or click events or, or, or these other things that PostHog just does for you. And you want to let PostHog do that job for you. So in order for it to work, and I have tried it both ways, You need to copy this and then we can go over to your WeWeb. So what they'd say to do is go put this into, you know, native code inside your native code element inside your WeWeb, and they'll walk you through how to do that here. But in the case of WeWeb, we're going to want to pause. Want to add it as custom code that will go onto our site here. And so again, we curl path, pause, have it on custom code that will go on our site. So we can click on here and the pages and whatever page you want to attach to. In this case, I'm attaching to my homepage. I believe that we can have additional things might go outside the page, but going onto the homepage is probably be good enough because as we go from page to page as a single page application, it should still be tied to that same page. So I can go to custom code and their recommendation over here is they say put it ideally just above the /head tag. That's fine. That's actually by default the way WeWeb adds things when you say head. So if you go over to WeWeb, you can add custom code here. Now, you will remember that I've often said in the past in these videos that putting things in custom code is the slow way to do it. It means you have to publish a site in order to see this code actually run. And that's, it's pretty annoying because it slows down our development lifecycle. So the, so it will behoove us to do, to think about how we might be able to speed up that cycle after this. If I do that, great, I've now added the code to the site. Now, the, the next thing it asks us to do is to create a button. And we're gonna create a button and we're gonna call it main CTA, just like we have over in the corner. So I'm going to go back to workflow test and I'm going to say, let's add a button. And we'll just call it, uh, click me. And that's fine as far as it goes. And then let's take a look at the next part of the documentation. And they're saying to put this code directly into your custom code. So we'll do this again. This is the kind of code that actually we could put into the page load as well if we wanted to, but it's giving us a particular approach. Let's just follow their approach for a second. Oh, they want to go under the body tag. Okay. Script. Great. And we will say this works fine. Okay, great. Oh, the other thing it tells us to do is we need to change this to have an ID of some kind in order to run their particular exam because we're gonna be doing it by ID main CTA.. And so that's part of what they do. We say choose main CTA as the ID in here. And we need to do that over here in WeWeb. And great. And now publish and publish. Okay. So it published. And now let's go back over to here. Where I've also opened up DevTools. And the way you can open up DevTools is by clicking on, by right-clicking on the screen, clicking Inspect. You can also do it through a keyboard shortcut. And it still says click me. So I'm actually not even sure whether it's working. And this leads us to a significant problem. The, we're about to have a very long loop of testing because we're going to make a test in WeWeb and then have to do a publish and send it over. Gotta tell you, this thing just took 4 minutes for it to publish. So we want to tighten up this cycle. So the thing I would recommend doing is going to be using a different state change tool. This is not one of our free ones, but it is part of our community. So anyone's part of our community can use it, which is called ScriptTag. And ScriptTag, let us, let's just make a new, I'll call this YouTube, oop, PostHog, right? That way we'll be able to identify what it is. And we'll just say console.log hello. I like to do this as a way to just make sure the whole thing is working. Now, I have mentioned before that we have this thing called copy WeWeb snippet that will copy to a clipboard so you can just add it to the load event in WeWeb. Now, the reason why we do that is so that you can be using this code when you're in the WeWeb editor. However, the way that post hoc works, it will only ever do anything if you are in published mode. That's the reason why we're going through this pain of publication. So in that case, what we really want to do is copy an actual script tag, just like we would if this were, say, a Webflow version. So I'm going to go back over to my, my WeWeb editor again, and I'm going to go back into custom code. And here, instead of running the code they gave us, I'm gonna say just include my script tag that will then include the code that we're editing over here. See, that way whenever we make a change here, it'll be automatically available over to the published version of the WeWeb site, and I won't have to go through this publication process over and over. From your point of view, it might be very similar 'cause I'll fast forward through the publication parts. But from the point of view of the editor, a person actually doing the work, it is a lot more time. So let's, let's bring that to work. So we put this over into WeWeb, we put this into the body. Good stuff. Now let's publish that and say publish. And we will say 1, 2, 3. And we'll get going. All right. Now we've published that. This hopefully puts us in a position of having loaded in the PostHog stuff and having loaded in the script tag part as well. So let's find out if it worked. Now remember what I put into here was just a console.log, right? So the way we'll be able to validate this is working at all is if we see that console.log of a little bit of hello. So let's refresh the page. Oh yeah, you see the hello up there and then watch this. I can now do hello. How are you? I don't have to do the prettification. I just do it to make sure I didn't make a mistake. And then I can go back over to here and I can refresh. Again, see it says hello, how are you up here in the, in the dev tools. Now, the reason I did this was so that we can more quickly bring in code that the footbill at PostHog suggests that we should be running here. So we had the main CTA. So now let's add our code over here. We'll say post on feature flag. So let's go back over to here and we'll say, and we just sort of add the code just like we did with anything else. And we will go over now to here and we will click refresh. It says, hello, how are you? But it still says click me up there. Let's just make sure that the ID took correctly. We'll inspect and we see a button here. It says main CTA. Good. And now there's a question like, why doesn't this code work? How about this? Let's find out. Did it actually fire? So we'll just do console.log feature flag starting, and we'll say console.log, this is the test cohort. You see the test that's running right here is whether or not the feature flag associated with cohort, associated with PostHog, and associated specifically with the CTA feature flag or experiment is called test. In the event it's called test, does one thing. In the event it's called control, which is our other big option. And I think over here, let me show you, let's see, experiments, we have test and oh, I might be under feature flags. Yeah. Okay. Here's CTA here. Yeah. Good. Yeah. CTA. And yeah, so I have control and I have test underneath feature flags. And basically that, that's all a Postdoc is going to do is to indicate whether for this particular feature flag, the CTA one, I'm in the control group or in the test group. Of course, you can set it to 90/10 or whatever if you're just trying to give this feature to a few people. You can also decide whether or not you want this to be persisting across authentication events, etc. That's all more advanced use of, uh, post-hoc. We won't get into that today. But what this will do is it will say, all right, I'll keep it on the log if it finds that this is a test cohort. Then it goes and looks for a button with an ID of main CTA. Okay, fine. I'll say console.log, I found the main CTA ID thing, right? I'll say console.log updating the button text, and I'll say console.log updated the button text. Great. Let's see if that works. So we'll go to here and I will refresh the page. We'll do the console so we can just see clearly what's going on. Refresh the page. Hello, how are you? On feature flag starting, I'm test cohort, update the button text. I gotta say it didn't update the button text. Why not? And it didn't because, oh, this is not a thing. There's nothing called button.text. It's called button.innerText. So that was something we actually copied from the PostHog documentation here. Don't do that. Use, use innerText. So I will then say Prettify. Uh-huh. And let's try running it again and see if that works better. Refresh. Yeah, cool new CTA. And then it went back. You see how it went back? Why'd it go back? It went back because it updated the text early in the cycle. Let's try running it again. Yeah. So what's happening here is WeWeb has opinions about how all the rest of this is supposed to be drawn and WeWeb is doing an update to the button after we've done our work with WeWeb. So this kind of approach that they're encouraging for writing with Webflow, which is the one where we're directly modifying what's going on the, on the browser itself, is okay as long as you have direct control over the browser after the initial rendering. That's true in Webflow, but it's not true in WeWeb. So we want it then to be changing things up a little later in the process. We would like this code, actually not even about timing. What we want to do is do it in a way that where Willow's not gonna run around and try to change it on top of us. Now there, there's a way to do this. Okay? So a way to do this, I'll just stick our thumb in the dam for a little minute. We can do a setTimeout and we can create a little function that's just gonna wait for 2 seconds. And only then make the change, hoping that WeWeb does its business before this thing does its business. And if I were to go back to here and I refresh, yeah, you see now it changes into cool new CTA. But you see how we lost all the, all the little graphics and whatever? See, the reason for that is that the, if we were to actually look inside this, let's just tell it to not do it for a second. Comment that out and you see immediately that'll mean that it won't actually run it anymore, which is cool. You see how it got these other little bits here and we don't necessarily wanna change all those. See, changing the entire inner text of this replaced with that whole big piece of black text when in fact what we want to keep is the actual structure of it. So if we take a look inside what's actually inside this button, we have a button here, but we don't wanna just change it to have an inner text because it actually has a div and a span and a div. So we could do something where we're just gonna modify the span that's inside the div cuz that's the one that actually says click me. That way if we're changing that inner thing, maybe we don't get rid of all the styling. If we try that for a second, then what we can do is we can say const span equals button.querySelector span to find me the first span underneath that. And then I'll just set the span's innerText to cool new CTA. Try that. See how well that works. That's right, don't need that, need this. Great. And now, see, it now shows cool no CTA in a way that's much more compatible because we're editing the right element inside as opposed to editing the top level. But again, we're modifying the DOM itself. Sorry, the domain object model. That's the way that the browser thinks about what's drawn on the page. And WeWeb has its opinion about how that thing should be drawn. And this little bit of JavaScript code we're running has its opinion about how it should be drawn. And, and they're getting into fights, right? The reason why we did this little setTimeout was just try to make our guy go last, but that's, that's actually weak tea as an approach. So the better way for us to approach feature flagging in WeWeb is not going to be through direct modification of the domain object model, but it's going to be through setting variables in WeWeb. So let's talk about how we do that. So over in WeWeb, we can say this thing, instead of having a text that directly is called text right now is directly called click me, right? When we're clicking the text to edit. But we could instead say, let's make it attached to a variable. So if we click on the cans over here, we click on variables, say add a variable, we're gonna give it a name and the variable's gonna be button text, right? And the, the type will be a text and we will say, please click me. And now if I go over to here, I can click on the little plug and connect to the variable called button text. Variables, button text. See, it goes, becomes button text, and now button text becomes please click me. Now this is important because WeWeb knows to update this every single time this variable changes because they're what's called, they're bound to each other. So we have an opportunity then from outside our code to be updating this variable.. Now, how do we do that? We do that through something called update_value in. So basically say, okay, we get to test and we're saying we're just not going to do any of the rest of this business. Let's get rid of that for a second. And we're going to say if we're set to test, then we're going to do, we know we can use urllib.variable.update_value, and this takes a key. And then we can change it to whatever we think the test name should be. For, what's gonna be the ID? Is it called, is it called button text? And the answer is no, it is not called button text. We have to copy the ID. Now you might not see copy ID here, and if you don't, that's because you gotta click on this little icon here, the one with the caret and the underscore. You see where it says hide dev information? If it shows, it says show dev information for you, then the experience you're having is it looks like this where you don't have copy ID. But if you click this and change it, make sure it says hide dev information. And no, I didn't make it say information. You can click over here and go over to variables and here, see, copy the ID, copy it. Great. And now I can go back over to the script tag and I can paste in that ID because that will actually update the ID with the has that variable that this thing is equivalent of button text. And we cover this in another one of our videos about linking WeWeb using, using the dubdub variable piece of dubdublib. Great. So now we have this and I do a prettification. It sort of lines it up for me, which is all very pretty. And then we're going to see if it will update to this is a test. Now, because we just made a change that's inside the WeWeb editor, we're going to need to do another republish. And you again are going to get to have fast forward, but this is why I try to code more stuff outside if we're going to need a publication process. We're published. Let's see how this thing works over here. Now, What should happen at the very start is it should say, please click me. And then if it is a test, let's just have a console.log and say I'm in test. 4.5. And then I'll go back over to Web App Test here. Great. The preview, because the preview is the only place where we can actually test this stuff. Console, and I'll do refresh. It says there's no such variable called 082. That's interesting. Let's go back over to here. Copy the ID. Is that the variable we're talking about? Sure seems to be. Okay. Let's refresh this. Now, 082, it says it doesn't find. So whenever I get this kind of situation, I start looking at www.lib.store. All right, let's find out what's inside the store. The store. Target here, data, data has variables, 082 and stuff. All right. All right. But here's what I think is going on. The variable looks like it exists in here, 082F, 082F, but I don't think it exists yet. See, this code is running right at the very beginning of the page. So I think it's loading when PostHog is ready to go, but I don't think it's loading when WeWeb is ready to go. Now, if we were doing Webflow, we'd probably do this inside of Webflow push. And a more generalized form of that, which works perfectly fine here, is going to be window.addEventListener('load'). And this will cause it to basically be on the page load event. Basically the same situation if you were using the workflow that's inside WeWeb on the no-code front. So here I can just say inside the load, so make sure you've loaded, only then go checking for the feature flags, etc. Let's see if that works. Refresh. Ooh, that's much better. Oh, look, hey, it says this is a test. Did exactly what we wanted to. Now what can I do? This is a test. My friend. Printify. And back to here and refresh. So it does identify that this is a test. When we are in test mode, it changes it to whatever text we want is, and we're able to do that using a WeWeb variable, which is much more the WeWebby way of doing it. And I think that lets you run the next mile here. So the key issues are going to be about patience, making sure that you have this double event listener going. We're going to do it on page load and we're going to then wait for the feature flags. When the feature flags have hit, we're looking for a particular feature flag, i.e., does the one, does the type that we're looking for equal the cohort that we're looking for? And then the event that we're in a certain cohort, we update the value and then allow WeWeb to do the rest of this business. This actually makes it really convenient to be testing our things inside WeWeb. Because we could probably set up the dominoes a little bit further up in terms of having the, uh, this could do something more direct. So we could have, we could have inside our WeWeb another variable that identifies which cohort we're in. And then you can have your page sort of go back and forth based on that variable value. Like I could say add a variable and I could say cohort, or I could say call it CTA cohort. And don't need a folder for it, and I'll just call it a text, and I'll say by default it's control, create it, right? And then by looking at CTA cohort, I can copy that ID, and I could say something like lib.variable.update_value, and that could be my variable, and I could say this is HOST POGGETT, feature flag, CTA. So now it doesn't need to be inside my if-then, it's just, I'm gonna set that thing at the very top level. That way, if you want to be setting things that way, you can have the dominoes fall inside your editor. If you wanna do more direct edits to your variables, you're able to do it through just setting the variables here. And of course, we already talked through the, the final and somewhat more hacky approach, but still available to you and can be quite useful if you don't wanna have to do publication again. Of just looking for things using your query selectors or what have you in order to go modify elements on the page directly. This allows you to do feature flagging. This allows you to do experiments and A/B testing and be getting really full use out of the telemetry systems like PostHog. So I hope this was helpful. And this is the kind of very hard thing that we work on the daily over at StateChange, both with our office hours and of course all these well over 1,300 recorded videos from these office hours. That people are using to be able to move a lot faster. I hope you'll check us out and I'll talk to you next time.










