Editing Pages

From SocialStack

Pages in SocialStack are a content type with a JSON structure that describes the components on the page. We call this JSON structure canvas JSON. A page can be both a singular URL, such as an about page on your website, or multiple, such as a page which displays blog posts.

That's quite a bit to unpack, so let's get into the details.

What actually is a page anyway?[edit | edit source]

Pages started life like most other modules in SocialStack - via the generate command. This means it fundamentally is no different from other modules, aside from it is simply included in the list of default modules as "Api/Pages". You can actually uninstall this module and you'll end up with an API only headless instance. This also means that they use an automated database structure as well as a service.

Pages have a database stored field called BodyJson which is a textual value that contains JSON. Canvas JSON is a very light representation of a tree of components which, when rendered by the Canvas component on the frontend, results in your page on the screen.

Pages also have a URL field which indicates simply which URL(s) this page will be served for. For example, a page with a url of /about will be served on example.com/about. URLs can also support tokens such that one page can be used to display multiple dynamic objects - a blog or forum for example. See the section about tokens below for more information on this.

Canvas JSON is not specific to pages - it is used by emails too for example. The canvas JSON editor can be used by any content type by creating a string field which ends with "Json".

How the editor works[edit | edit source]

The canvas editor itself is designed to be a what-you-see-is-what-you-get experience, but there are some things to be aware of.

Firstly, it has an add component window which lists available components on the site. This window will only display components which have React propTypes set. It can be an empty object; it just needs to be not null. When generating a new UI component with the generate command, it will also include a commented out propTypes too.

Secondly, the propTypes are directly used to establish what options a component has. For example if you drop an image onto the canvas, the UI/Image component has propTypes which include which actual file to display. A generated component also has some examples of these too.

Finally, the editor does also have a source view which lets you see and edit the underlying canvas JSON instead. This can often be useful for quickly fixing a layout which is otherwise awkward or ambiguous in the visual view. Check out the guide on canvas JSON for more details on valid content here.

URL Tokens[edit | edit source]

You may have a page which displays, for example, blog posts. This is achieved by using tokens in your URL, like this:

/blog/{blogpost.slug}/

The value of a token is usually {contentType.fieldName} meaning tokens will work for most content including custom types. It doesn't have to be a content type though - you can also use arbritrary token values, like this:

/password-reset/{token}/

The value of this token is accessible in the same way (see the section below).

You can use multiple tokens in a single URL too, like this:

/blog/{blog.slug}/{blogpost.slug}/

Accessing the values[edit | edit source]

On the frontend the token values are available via the page router, which can be used via a React hook or consumer:

import {useRouter} from 'UI/Session';

...

var {pageState} = useRouter();

// pageState.tokens, pageState.tokenNames

The tokens is an array of each token discovered from the URL.

Alternatively a token resolver can be used which allows a CMS/editor friendly textual representation of the tokens as well:

import {useTokens} from 'UI/Token';

...


// Assuming the original url was /blogs/{blogpost.slug}
// This would typically be a prop so someone can change the text with the editor
var inputText = 'The blog slug in the url is ${url.blogpost.slug}';

// Swaps the ${tokens} with their underlying value.
var processedText = useTokens(inputText);

return <h1>{processedText}</h1>;

The token resolver works with either {typename.fieldName} or {typelessTokens} in the URL as well.

Primary content type[edit | edit source]

If you use at least one content type token (such as {blogpost.slug}) in your URL, the very last of these tokens in the URL will be resolved to the content object it refers to, and this object is then called the primary content of the page.

For example going back to blog posts:

/blog/{blogpost.slug}/

The "primary content" of the page at /blog/hello-world/ will be a BlogPost with the field called "Slug" equal to the value "hello-world". The primary content on the page has some special use as it can be used in the page title, description etc amongst other things.

For convenience in React, you can quickly access the current primary content on a page like this:


import Content from 'UI/Content';

...

<Content primary>
{primaryObject => {
  if(!primaryObject) {
    return 'Loading..';
  }
  
  // primaryObject is e.g. a BlogPost, depending on which page we're on
}}
</Content>

If you need the URL for a piece of content, it must be the primary object on at least 1 page. So long as such as page exists, PageService.GetUrl will return the backwards resolved URL for you. Note that because the rule about the "very last" token above is important as it means you can have multiple pages with the same primary content. A situation where this happens might be, for example:

  • /blog/hello-world/
  • /blog/hello-world/comments

And similar "sub pages" for a piece of content. Because of this, the shortest one wins as being the one which will be used for the contents URL. I.e. if you ask for the URL for the blog with the slug "hello-world", then its URL would be "/blog/hello-world/".

Page title[edit | edit source]

The title field of a page is used for setting the title bar of the page and will also appear on e.g. search results. If your page has primary content on it (see above), you can use tokens in the page title as well. Again continuing with the BlogPost example, that may be simply the title of the post:

My Great Site | {blogpost.title}