PoP is an open source framework for building progressive web apps, decentralized websites and social networks.

Check the demoOpen in GitHub

PoP combines WordPress and Handlebars into an MVC architecture:

Show me how it works

Core features

The PoP engine automatically provides the website of its own API, just by adding output=json to any page’s URL.

Key benefits:

Documentation-less API: the API needs no documentation, because simply removing parameter “output=json” from its URL renders the website, displaying the information for GET requests and forms with their required fields for POST operations.

Simplicity: the API URL can be calculated directly from the website URL.

SEO friendly: the API URL keeps WordPress’ permalink structure and, hence, the slug’s high ranking.

API-ready: the API can also be consumed by third-party systems, mobile phone apps, etc, without further work needed by the developer.

Because they comprehend the same JSON structure, PoP websites can natively interact with each other, fetching and processing data from each other in real time, for both GET and POST requests.

The calendar below is displaying events from a different website (source calendar here). Clicking on a link inside the calendar opens the page locally, not in a new tab (check it out!).


Decentralization: the application’s data sources can be split into several websites/domains

Aggregation: content from a variety of PoP websites can be aggregated and displayed together

Component microservices: components/libraries can be made available to developers as SaaS

Multiple views to display content

Such as a list or grid of items, full view into each, or you can create your own custom views.

Leonardo Losoviz

Founder of PoP

Buenos Aires

Jun-E Tan

Running around with a hammer

Kuala Lumpur


Break the information monopoly

All Locations Big Ben | Camp Nou | Coloseo Romano
Buenos Aires

Leonardo Losoviz

Founder of PoP

I'm the founder of the PoP framework. I've been working in this project since April 2013, when developing the website for MESYM (https://www.mesym.com), an environmental NGO in Malaysia which I co-founded. Later on, I've used the software to launch other projects, namely TPP Debate (https://tppdebat... Read more

Kuala Lumpur

Jun-E Tan

Running around with a hammer

I am a freelance researcher on issues related to sustainable development. I write, I talk to people, I try to make as big a difference as I can. I've been running MESYM.com since 2013, an online platform for environmental issues in Malaysia. My motivation for working on MESYM.com is to amplify all t... Read more

All Locations Big Ben | Camp Nou | Coloseo Romano


Break the information monopoly

the PoP framework is open source software which aims to decentralize the content flow and break the information monopoly from large internet corporations.

Leonardo Losoviz

Jun-E Tan


Plugins can easily integrate with PoP to add their own views. Eg: the map below is done by integrating with Events Manager.
Scroll right to load more results

Leonardo Losoviz

Founder of PoP

Buenos Aires

Jun-E Tan

Running around with a hammer

Kuala Lumpur

Miguel Angel

Helping to demonstrate PoP

Eiffel Tower

John Swimmer

Helping to demonstrate PoP


Pedro Selva

Helping to demonstrate PoP


José Sen

Helping to demonstrate PoP

Red Fort

Mónica Ling

Helping to demonstrate PoP

CCTV Building

Marta Qian

Helping to demonstrate PoP

Great Wall

Roberto Bebé

Helping to demonstrate PoP

Disneyland Hong Kong

Manuel Shimano

Helping to demonstrate PoP

Botanical Garden

Rodrigo Baboon

Helping to demonstrate PoP

City Palace

Colorado Márquez

Helping to demonstrate PoP

Central Park


Break the information monopoly

All Locations Big Ben | Camp Nou | Coloseo Romano

Multiple presentation styles

PoP themes have different “modes” or presentation styles, used for displaying printing or embeddable versions of the website, alternative layout designs, etc.


The same page can be open in different sections on the page, such as sidebars, floating windows, modal windows, or your own creations.

Open “PoP Blog”...

Tabbed browsing

Similar to a browser, the website keeps all pages open, showing tabs to switch among all of them.



Most content in the website, by nature “dynamic” or mutable, is made “static” or immutable by serving it through a Content Delivery Network (CDN), thus making the best of both worlds: content is dynamically generated through WordPress, but it can be cached as coming from a static site generator.

This feature has the following advantages:

Cheaper hosting: websites with high traffic can reduce their hosting costs, since content is mainly served from the CDN instead of the server.

Faster: CDN data-centers are located near the user, thus greatly reducing latency and boosting the overall website speed.

WordPress is still there: content is still created dynamically, through WordPress. The static version of the website is generated on runtime.



Through the implementation of service workers, PoP provides offline first capabilities, with the following benefits:

  • the website functions offline
  • performance increases by reducing network requests
  • pages load immediately (from the second access onwards)

Service workers are not yet implemented in all modern browsers, however both Chrome and Firefox (the two most popular browsers, with a combined market share of more than 70%) do support it.

Social network features

Users can post content directly from the front-end. In addition to creating posts and custom-type posts (eg: events), users can also submit external URL links, which will be embedded directly from their source.

Content features

Multimedia: add images, image galleries, Youtube videos, tweets, and other embeds.

#Hashtags: categorize the content by adding hashtagged words in it; each hashtag has its own permalink, under which is shows all content tagged with it.

@Mentions: the user writing content can @mention any other user inside the post, and this person will receive a notification.

To embed a video or another object, simply place its URL into the content area. Make sure the URL is on its own line and not hyperlinked. For example:

Check out this cool video:


That is a lovely tango!

You can embed URLs from all these services:

Flickr, Instagram, Reddit, SlideShare, SoundCloud, Spotify, Twitter, Vimeo, Vine, YouTube, and others.

Inter-linking: the post can be related to any number of already existing posts, giving a proper context to the post created.

Co-authoring: the post can have any number of co-authors, which will be also given rights to edit the post.
Custom metadata: plugins can add their own metaboxes. Eg: the event post type, from Events Manager, can add fields for date/location.

Content moderation

Content posted by the users can be published immediately, or await for admin’s approval for publishing. In case the post is published immediately, a “Flag inappropriate content” mechanism is in place for the community to moderate the content.

Content updates

After posting new content, all users concurrently browsing the website (either logged in or not) will see a pop-up notifying them of the update.
View 1 new post
Scrolling down displays a never-ending stream of fully-loaded, yet partially-minimized, content items.

Subscribe to topics

Users can subscribe to #hashtags, and receive notifications whenever new content is tagged with them.

Follow users

Users can follow other users, and receive notifications whenever there is any new activity associated to them.
Buenos Aires

Leonardo Losoviz

Founder of PoP

I'm the founder of the PoP framework. I've been working in this project since April 2013, when developing the website for MESYM (https://www.mesym.com), an environmental NGO in Malaysia which I co-founded. Later on, I've used the software to launch other projects, namely TPP Debate (https://tppdebat... Read more

Recommend posts, extract and up/down-vote highlights

Users can recommend posts, extract highlights from them, and up/down-vote these highlights.
API Data: exporting all data in the website to be consumed by others

API Data: exporting all data in the website to be consumed by others

It is now possible to export all data in the website, through the addition of the share button “API Data”. This way, your PoP website truly becomes a platform, from where 3rd party developers can, through the public API, consume your information and show it on their own websites, interact with mobil… Read more

Users receive real-time notifications concerning any activity related to them and their network (i.e. their followed users + those users who are members of the same communities), such as:

  • a followed user posts new content
  • a subscribed topic receives a new post
  • a recommended post gets a new comment
  • the user has a new follower
  • etc


These are the general notifications. Please log in to see your personal notifications.
PoP allows the creation of user roles, for granting special powers to the users, on a user-by-user basis. For instance, members may be given access to premium, behind-a-paywall sections.
The user below has been granted role “Community”, for which this user can accept members in the website.
All Locations Big Ben | Camp Nou | Coloseo Romano


Break the information monopoly

the PoP framework is open source software which aims to decentralize the content flow and break the information monopoly from large internet corporations.


PoP allows the creation of highly customized filters, for all type of content (posts, users, comments, tags).


Development features

PoP components were designed to be separated and recombined effectively, in several dimensions:

All code is organized into modules. A module is either an atomic/reusable functionality, or itself a composition of other modules. In its .php file, each module defines:

  • its submodules
  • its configuration (classes and styles, text strings, links, etc)
  • data fields to retrieve from the queried database object
  • what javascript functions to execute after appending to the DOM
class PoP_PostLayoutProcessor {

  function get_name() {
    return 'post-layout';

  function get_submodules() {
    return array(

  function get_template_configuration() {  
    return array(
      'classes' => array(
        'div' => 'alert alert-info'
      'strings' => array(
        'title' => __('Title:'),
        'url' => __('URL:')
      'module-ids' => array(
        'layout-details' => 'post-details'

  function get_data_fields() {
    return array(

  function get_block_jsmethods() {
    return array(
      'img-content' => 'imgResponsive'

// Initialize
new PoP_PostLayoutProcessor();

PoP gets the configuration and the data-fields from the modules, queries the database, and sends the results to the client as a JSON code.

In the front-end, PoP creates a context containing the module’s configuration and the queried object data (under itemObject), and executes the corresponding Handlebars javascript template to generate the HTML code:

<div class="{{classes.div}}" id="{{generateId itemObject.id}}">
  <p>{{strings.title}} <strong>{{itemObject.title}}</strong><</p>
  <p>{{strings.url}} {{itemObject.url}}<</p>
  <div id="{{generateId itemObject.id group='img-content'}}">

  {{#withModule . module-ids.layout-details}}
    {{enterModule ../.}}

(Please notice that the current module ‘post-layout’ is executing submodule ‘post-details’, producing its HTML to be nested inside.)

After appended to the DOM, javascript methods are executed on the corresponding elements, passed by PoP under args.targets:

popPostUtils = {

  imgResponsive : function(args) {

    var targets = args.targets;

// Initialize
popJSLibraryManager.register(popPostUtils, ['imgResponsive']);

In summary, each module is composed of:

  • A PHP object defining the module’s configuration
  • A Handlebars .tmpl template, with the view of the module
  • Javascript functions to execute on the elements added to the DOM
  • Styles defined in .css files

There are different types of modules, namely: topLevel, pageSection, block, blockGroup and module. They have the following hierarchy:

  • 1 topLevel contains N pageSections
  • 1 pageSection contains N blocks/blockGroups
  • 1 blockGroup contains N blocks/blockGroups
  • 1 block contains N modules
  • 1 module contains N modules (ad infinitum)
It is the single, top-level module loaded by the application. It is similar to WordPress’ hierarchy template (Single, Home, Archive, Author, etc).


It is a physical/functional section on the website, either visible or hidden (eg: modals, offcanvas, etc).


It is an independent component, which keeps a state; it can be added to different pageSections and customized for each.


It is a special type of block, which collects/wraps other blocks and allows to synchronize state among them.


It is an atomic/reusable functionality, or a composition of modules; modules are the building blocks of a PoP application.


PoP operates in a strictly top-down manner: parent modules know what inner modules they contain, down the line to the last module, and can change those modules’ properties; however, inner modules do not know who is including them, and do not need to care either.

When initializing properties, parent modules take precedence over inner modules: when a module initializes itself, if any one property has already been set by a parent module, then that value will be kept. This way, PoP allows for dependency-injection, where each module can set its default attributes, yet allow for properties to be injected externally in advance (possibly coming from a configuration file).

class GD_EM_Template_Processor_ScrollMapBlocks extends GD_Template_Processor_BlocksBase {

  function init_atts($template_id, &$atts) {

    // $template_id = current module
    $this->add_att($template_id, $atts, 'direction', 'horizontal');

    // 'map-layout' = submodule
    $this->add_att('map-layout', $atts, 'direction', 'horizontal');
    return parent::init_atts($template_id, $atts);

The decentralization features of PoP allow to create an application architecture in which different components are being served from different domains:


To implement it, simply set the external domain source on the block. This configuration value can be defined directly in the block, or it can be injected externally, in which case the block is unaware where it is fetching its data from.

class GD_Template_Processor_SectionBlocks extends GD_Template_Processor_BlocksBase {

  function get_dataloadsource_domain($template_id, $atts) {

    switch ($template_id) {
        return 'https://demo.getpop.org';

    return parent::get_dataloadsource_domain($template_id, $atts);
class GetPoP_Template_Processor_CustomBlockGroups extends GD_Template_Processor_ListBlockGroupsBase {

  function init_atts_blockgroup_block($blockgroup, $blockgroup_block, &$blockgroup_block_atts, $blockgroup_atts) {


      $this->add_att($blockgroup_block, $blockgroup_block_atts, 'dataloadsource-domain', 'https://demo.getpop.org');

    return parent::init_atts_blockgroup_block($blockgroup, $blockgroup_block, $blockgroup_block_atts, $blockgroup_atts);



PoP provides optimal rendering capabilities, allowing to render the HTML in the backend when loading the page first time, yet rendering all code in the frontend through javascript from the 2nd page-load onwards.

It is isomorphic: the code used to render the HTML in both front and back-end is the same: precompiled Handlebars javascript templates, rendered in the front-end using the Handlebars runtime library, and in the back-end using LightnCandy.


SEO: all search engines can perfectly parse the site’s HTML.

Integration with technologies and systems: server-side HTML rendering is needed for implementing new technologies such as AMP and microformats, and for broad system interoperability.

Speed: the first page is loaded much faster, since it doesn’t need to wait to load javascript templates to render it. Starting on the 2nd page onwards, javascript takes over, rendering faster and making the website dynamic.

Re-usability: developers implement the code only once and use it everywhere: front and back-end, emails, etc.

PoP queries the database and recreates the data structure in the JSON code sent to the front-end, keeping the relationships among all different objects (eg: if two different posts share the same author, the author’s data is output only once, and the posts reference the author through the id).

The produced JSON object has key ‘database’, containing the data for all objects queried in the request; key ‘dataset’, with the ids of the result objects for each block; and key ‘settings’=>‘db-keys’, with the top-most object domain (“posts”, “users”, “locations”, etc) for each block.


Viewing the response’s JSON code in Chrome’s Console. Pay attention how under key ‘database’ the object data is relational, and objects from different domains have also been fetched: the post’s author (with id 851) and the post’s location (with id 23494); and under key ‘dataset’ it indicates the result object ids for each block (posts with ids 23787, 23784, 23644 for the main block)

PoP integrates with WordPress mainly in the back-end, executing WordPress functions to fetch data. Implementing a PoP website requires the implementation of these objects to communicate with the WordPress API to fetch the data. PoP already ships with implementations for the most basic object types (posts, users, tags, comments) and some custom post types (events, locations).

Basic example

Below is a description of setting up some required components: a “block”, a “dataloader”, some module defining “data-fields”, and a “fieldProcessor”.

A “block” is a special module in PoP which defines the configuration for fetching data from WordPress, such as what “dataloader” will be invoked, how to filter the query, the data’s source URL, among others:

class GD_Template_Processor_SectionBlocks extends GD_Template_Processor_BlocksBase {

  function get_dataloader() {


  function get_dataload_source() {

    global $author;
    return get_author_posts_url($author);

  function get_dataload_query_args() {

    global $author;
    return array(
      'author' => $author,
      'cat' => 55,

// Initialization
new GD_Template_Processor_SectionBlocks();

A “dataloader” executes WordPress functions to fetch data from the database, allowing to set the args in the query to filter the results.

define ('GD_DATALOADER_POSTLIST', 'post-list');

class GD_DataLoader_PostList extends GD_DataLoader_PostListBase {

  function get_name() {
  function get_query($vars = array()) {
    $query = array();
    if ($cat = $vars['cat']) $query['cat'] = $cat;
    if ($author = $vars['author']) $query['author'] = $author;

    return $query;
  function execute_query($query) {

    return get_posts($query);

// Initialization
new GD_DataLoader_PostList();

What data will be retrieved from the queried object is defined in all modules, through specifying what “data-fields” they need.

class GD_Template_Processor_PostLayouts {

  function get_data_fields() {

    return array('title', 'url', 'comments-count');

// Initialization
new GD_Template_Processor_PostLayouts();

The “data-fields” are processed and resolved through an object called a “fieldProcessor”.


class GD_DataLoad_FieldProcessor_Posts extends GD_DataLoad_FieldProcessor {

  function get_name() {
  function get_value($resultitem, $field) {
    $post = $resultitem;

    switch ($field) {

      case 'title' :
        return get_the_title($post->ID);

      case 'url' :
        return get_permalink($post->ID);
      case 'post-type' :
        return $post->post_type;

      case 'comments-count' :
        return get_comments_number($post->ID);

      case 'cats' :
        return wp_get_post_categories($post->ID, array('fields' => 'ids'));

      case 'tags' :
        return wp_get_post_tags($post->ID, array('fields' => 'ids'));

    return null;

// Initialize
new GD_DataLoad_FieldProcessor_Posts();

In summary, adding the required objects to implement a WordPress website using PoP is a straightforward process, and the code already ships with plenty of implementations.

Open all
In addition to caching the HTML response, the website caches the settings for all similar posts. This way, upon requesting two posts belonging to the same post type (such as events), the second post will access the cached settings and need only retrieve the object data from the database.
All pages have been stripped of all user information related to them (eg: has the user recommended it?). All such user information is loaded on a subsequent, immediate request.

As a consequence, requesting the page produces the same response for all users, allowing most pages in the website to be cached, independently if the user is logged in or not; only those pages showing user-specific information, such as My Content, are non-cacheable.
Selected content, such as comments added to a post, can be loaded only after the page has loaded, allowing for a faster initial page load.
The software pre-loads specified pages, running in the background, so they are already loaded by the time the user clicks on the corresponding link. Eg: when clicking on the following links they will open immediately, since they were preloaded: Add Post, Contact us. Once these pages have loaded, if available, their code is stored in the browser using Local Storage.
Clicks can be intercepted in the front-end, and choose to do something instead of sending the request to the server. Eg: adding a post can open in a new page, or be intercepted and open immediately in a floating window.
When the user clicks on a link, PoP intercepts the request to submit it using AJAX. PoP can add extra parameters to the URL, which are not in the original link (and so hidden to the user), such as: language, theme, timestamp, etc.

Explore PoP websites in the wild:

Implement your website

Brought to you by…


Leonardo Losoviz is the creator of the PoP framework, and has been working tirelessly on it since 2013.

Read more about us and the story behind PoP.

Welcome to the PoP framework!
Break the information monopoly

the PoP framework is open source software which aims to decentralize the content flow and break the information monopoly from large internet corporations. Read more.

Sign up to our newsletter: