Repository navigation
Description
PostHog Apps
By now I'll assume you have all seen the demo from PR #9458 feat(apps): very dodgy frontend apps implementation
That's a dodgy PR, but shows what's possible. There, plugins gain a new frontend field, whose JS is separately downloaded, parsed and loaded on the frontend. It currently exposes a sidebar label, a React component, and a Kea logic connecting it, which can already be used to override breadcrumbs. All of that is injected on the fly, but, in a dodgy MVP way.
A lot is missing, including injecting code into your site, and any kind of persistence.
I see a lot of potential in this, and having spoken to many people about this, most share the enthusiasm, especially @jamesefhawkins and @corywatilo
The vision
This means different things to different people, but, as a website owner, I will add posthog to my site, and that's it. Everything else that my site will use, will come through PostHog. I'll sign up to PostHog, add the widget, enable session recordings, and comments (through the squeak app). Later on I'll inject the google analytics JS snippet through the PostHog Tag Manager app, and show Google just who's the boss here.
Basically, PostHog with its extra list of apps that I can pick depending on the needs of the site I'm instrumenting, will become the backoffice of my entire static website.
What's an app
After speaking to a few people (incl @corywatilo last night), we seem to now all be on the same page as to what's an app and what's not an app.
Basically: if you can build it as an app, it's an app.
The flip side: you can't really build anything as an app yet. Except plugins. All plugins are apps. We should rename the tab, and eventually give it a facelift. Just some apps can very soon have a frontend/sidebar component, and can soon after do even cooler things (HTTP requests, persistence, inject code on your site).
This means everything that's currently there in the PostHog App, remains as part of the PostHog App, as you can't build any of it as an app. Insights, feature flags, session recordings, etc are all core features of the PostHog platform that PostHog Apps can use as building blocks. The example is the "squack" app from above, which integrates a custom insight.
The "Web Performance" page as it stands now could be an app. It's built using existing components (basically the events list), and talks to existing APIs. You could enable it, and 1) something would start tracking performance data on posthog-js, and 2) you'd get a new page in the sidebar that lets you explore this data.
Next steps
Here are the next steps to make PostHog apps a reality:
-
Create a separate plugin installation step, which includes transpiling, bundling, and caching JS/TS. Integrate
esbuildinto the plugin server, and bundle thefrontend.tsxfile orfrontend/folder into a JS file we can import on the frontend. -
Replace the
evaldodgy AFproof of concept with a solution that imports the bundled plugin frontend scripts as ES modules via HTTP. These will be the cached JS bundles from the previous step, simply served from postgres. -
Rename the "plugin editor"'s two source windows to "Backend" and "Frontend" (or into three - "Ingestion", "Backend" and "Frontend"?), and allow users to write React components right within the PostHog app.
-
Make a stable and synchronous
django<->plugin servertask bridge ... or expose a HTTP endpoint directly onto the plugin server. -
Add support for an
endpointsobject in the "backend" part of the plugin. Endpoints are, you guessed it, HTTP endpoints, which can be called from the frontend, through django, through redis, through piscina, into the worker VM, and back again. Slow, but make it work, then make it fast. These endpoints could be used to use thecacheandstorageAPIs available for plugins for persistence. -
Add support for dynamically injecting JS into your website through posthog-js, and exposing that as a feature for app developers.
-
Create a list of React components (e.g.
Lemon*) exported to apps, and polish the storybook for public consumption. -
Create a local development bundle, which spins up a comparable esbuild-based local dev server, connected to a local or cloud posthog API, and lets you build your app's frontend and its tests.
-
Eventually, 6 months from now, add support for payments and create a healthy ecosystem.
-
Also, dogfood the platform by building these high quality apps:
- Squeak / Comments
- In-site Notifications
- NPS Survey
- Product Tour / wizard
- Forms (bye bye google forms)
- HTML Injector (bye bye google tag manager)
- SEO Optimizer aka the HTML meta tag configurator
- Live World Map flippy spinny globe thing
Who will do all of this?
I'd be quite happy to take on some of the work, and expand the plugin app server with these cool new features in the next sprints. It's really not that much work 😅 .

Activity
neilkakkar commentedon Apr 21, 2022
Much hype! To add to high quality apps: This unlocks running front-end experiments without code changes, which is something product-owners-who-dont-want-to-wait-on-engineers really want!
joesaunderson commentedon Apr 21, 2022
Commenting from a Squeak background, it'd be great to be able to ditch Supabase.
How would the apps implementation work in terms of:
mariusandra commentedon Apr 21, 2022
@joesaunderson The answer depends mostly on if, and when, we can implement this part:
(@yakkomajuri is this a 1h pairing task?)
If we have this endpoint support, we can use the APIs available in a plugin for persistence. This includes
cache- a namespaced redis client, andstorage- postgres-backed persistent JSON storage.One approach we could take would be something like this:
Authentication can be solved by either implementing our own oauth client endpoints, or solving it as a common solution all plugins can use. Either way works.
If we don't have endpoints, then except for authentication, we could still build most of squeak with PostHog's plugins today. This approach would store all comments as PostHog events in ClickHouse via a simple
posthog.capture().Then
onEvent, we'd use posthog's ownapi.get(...)to get the list of events for this page from clickhouse, remove the ones that had a follow-up moderation event, and store that JSON blob in S3 for easy access. Squeak's frontend will then use the S3 bucket as a read source.We'll need to be careful with timings (the event might be queryable only a second after ingestion?), but some clever use of redis will probably suffice. The comments might also get huge on some pages, and spam can become a problem. I'm not fully sure what are the limits of this system.
This will also require a bit of innovation for authenticated events, to prevent abusers from creating fake comment moderation reports... but nothing complex. A plugin that compares a hashed key with a salt only your backend knows should be enough.
Yet, we might want to store all comments as posthog events anyway, just to be able to do nice charts... and perhaps so we would have a very scalable platform.
storageis postgres, and does have its limits...jamesefhawkins commentedon Apr 22, 2022
An annoying (perhaps the most annoying) thing with squeak! is we want users to be able to login with their PostHog (or whatever product they're using)'s credentials to avoid neeeding a separate squeak account just FYI if you think about auth
mariusandra commentedon May 31, 2022
I built an app that actually does something, the Notification Bar app:
It uses feature flag variants for persistence. Upon loading the app, we ask the API for a list of flags, and create one with the name
${config.flagKey}if it doesn't exist. We then store the payload as the key of the only variant on that flag. Because only alphanumeric,-and_symbols are allowed, we store the key as a base64 encoded string.There's a hook
useFlagPersistencethat abstracts away the gory bits, and providespayload,setPayloadandsaveChangesas functions you can call.To enable it on your site, you must copy/paste some code one time. There's no "auto-injection" or MITM-as-a-service like we previously discussed.
Here's the code:
{ "name": "Notification Bar", "config": [ { "key": "flagKey", "name": "Key of the feature flag this plugin will use", "type": "string", "hint": "Only alphanumeric with dashes and underscores", "default": "notification-bar", "required": false } ] }Potential issues:
useFlagPersistence,useAnnotationPersistence,useConfigPersistence?) to users via@posthog/apps-common. I can already see myself copy/pasting this code if I want to send other types of data to users.posthog.capture. This will store a lot of random metadata on each event. Should we have a secondary bus for passing data between an app and posthog-js?mariusandra commentedon May 31, 2022
Here's a near-term list of TODOs for making the current level of frontend apps spark joy:
<data>part indata:[<mediatype>][;base64],<data>key. This might already be there.@posthog/apps-commonpackageindex.d.tsfile that aggregates the types of whatever is exported fromsrc/packages/apps-common. This will be used by the plugin source editor.frontend.tsxviaplugin.jsongetCurrentTeamId()to the frontend app for API callsMedium term issues that we should tackle, felt when writing the notification bar app:
PluginStoragemodel that "ingestion apps" use viastorage.get. Should/could we expose this to the frontend apps directly?posthog_pluginstoragetable(id, key, value, plugin_config_id)also getteam_id?frontend.tsxis limiting.posthog.onFeatureFlags(...)code directly via/decideandposthog-js. This removes a manual step for the user, and allows ever more complex apps that auto-update. This also unlocks "Toolbar Apps".To be continued.
posthog-bot commentedon May 31, 2024
This issue hasn't seen activity in two years! If you want to keep it open, post a comment or remove the
stalelabel – otherwise this will be closed in two weeks.posthog-bot commentedon Jun 14, 2024
This issue was closed due to lack of activity. Feel free to reopen if it's still relevant.