How to create Custom Pages?

Hi, I want to add Sign In and Sign Up pages but don’t want to fetch from wordpress backend. What is the correct way to add such pages?

2 Likes

Hi @aeyzazkhan,

The best way to do that at this moment is to create a custom handler for the route of each page (handlers are explained here). In this case, the only thing those handlers should do is to add a property to their data object to identify that specific route in React and render whatever component you want.

For example, in the case of a sign up page in the link /signup/ , you would define a handler:

const signUpHandler = {
  pattern: "/sign-up/",
  func: ({ state }) => {
    state.source.data["/sign-up/"].isSignUp = true;
  }
}

You would add signUpHandler into the handler array inside the init action of your theme like this (init is one of the actions triggered by Frontity, is explained here):

// In your theme, add an init action:
actions: {
  init: ({ libraries }) => {
    libraries.source.handlers.push(signUpHandler);
  },
  ...
}

And then, in the index.js of your theme you can check if data.isSignUp is true and render, for example, a SignUp component.

<Body>
  {data.isFetching && <Loading />}
  {data.isSignUp && <SignUp />}
  {data.isArchive && <List />}
  {data.isPostType && <Post />}
  {data.is404 && <Page404 />}
</Body>

This way, your custom pages work the same as normal WP pages but without fetching anything from your WordPress API.

Let me now if you have any doubt or question.
Cheers!

4 Likes

Thanks, It worked like a charm.

2 Likes

In wich file I put those codes? It’s not clear for where I have to put the const signUpHandler, where I put the actions.

In the init action of your theme or extension.

If you haven’t renamed the mars-theme folder, that should be /packages/mars-theme/src/index.js.

For example, like this:

import Theme from "./components";
import image from "@frontity/html2react/processors/image";

const myHandler = {
  pattern: "/some-url/",
  func: ({ state }) => {
    // Add handler logic here...
  }
}

const marsTheme = {
  name: "@frontity/mars-theme",
  roots: {
    theme: Theme
  },
  state: {
    theme: {
      ...
    }
  },
  actions: {
    theme: {
      init: ({ libraries }) => {
        // Use html2react to process the <img> tags inside the content HTML.
        libraries.html2react.processors.push(image);

        // Add the handler to wp-source.
        libraries.source.handlers.push(myHandler);
      };
    }
  }
};

export default marsTheme;
1 Like

Thank you!! It works well!!

Awesome :slight_smile:

Just curious: what type of handler are you adding? What’s the use case?

1 Like

I’m trying to create a custom HomePage. I started with Frontity yesterday and I still taking time to get. My site will have one homepage and 3 custom pages for 3 custom post type.
How can I create a new route to a new component like we do in pure React??

My index.js

    import Theme from "./components";
    import image from "@frontity/html2react/processors/image";

    const before = ({ libraries }) => {
      // We use html2react to process the <img> tags inside the content HTML.
      libraries.html2react.processors.push(image);
    };

    const homeHandler = {
      pattern: "/",
      func: ({ state }) => {
        state.source.data["/"].isFrontPage = true;
        state.source.data["/"].isArchive = false;
      }
    }


    const marsTheme = {
      name: "@frontity/mars-theme",
      roots: {
        theme: Theme
      },
      state: {
        theme: {
          menu: [],
          featured: {
            showOnList: false,
            showOnPost: false
          }
        }
      },
      actions: {
        theme: {       
          actions: {
            init: ({ libraries }) => {
              libraries.source.handlers.push(homeHandler);
            },
          },
          beforeSSR: before,
          beforeCSR: before,
        }
      }
    };

    export default marsTheme;

If you use frontity.state.source.get("/") in the console of your browser, you can see the properties of the "/" link have changed (from the default ones) and now are:

So, you can now check for isFrontPage in your components/index.js file:

const Theme = ({ state }) => {
  const data = state.source.get(state.router.link);

  return (
    <>
      ...
      <Body>
        {data.isFrontPage && <div>Front Page!</div>} // <-- ADD THIS
        {data.isFetching && <Loading />}
        {data.isArchive && <List />}
        {data.isPostType && <Post />}
        {data.is404 && <Page404 />}
      </Body>
    </>
  );
};

Remember that you can also use state.source.homepage and state.source.postsPage in your frontity.settings.js file to redirect "/" to a WP page and redirect the blog to another URL:

module.exports = {
  packages: [
    "@frontity/mars-theme",
    "@frontity/tiny-router",
    {
      name: "@frontity/wp-source",
      state: {
        source: {
          api: "https://site.com/wp-json",
          homepage: "/about",
          postsPage: "/blog"
        }
      }  
    }
  ]
}

Those are the equivalents to the homepage WordPress setting:

35

Hi guys

I’m trying frontity for the first time today, I wanted to create my own page “agencies”. I tried to follow your indications, but I might be missing something.

in my src/index.js

    const agenciesHandler = {
  name: "agencies",
  priority: 10,
  pattern: "/agencies/",
  func: ({ state }) => {
    state.source.data['/agencies'].isAgencies = true;    
  }
}

const marsTheme = {
  name: "@frontity/mars-theme",
  roots: {
    theme: Theme
  },
  state: {
    theme: {
      menu: [],
      featured: {
        showOnList: false,
        showOnPost: false
      }
    }
  },
  actions: {
    theme: {
      init: ({ libraries }) => {              
        libraries.source.handlers.push(agenciesHandler);
      },
      beforeSSR: before,
      beforeCSR: before
    }
  }
};

in my components/index.js I have this:

...
      <Body>               
        {data.isFetching && <Loading />}
        {data.isAgencies && <Agencies />}    
        {data.isArchive && <List />}
        {data.isPostType && <Post />}
        {data.is404 && <Page404 />}         
      </Body>
...

When I tried to go to localhost:3000/agencies I get a 404. any ideas of what I could be missing?

I found a solution. I think I’m starting to understand how this works… at least the beginning.

const agenciesHandler = {
  name: "agencies",
  priority: 10,
  pattern: "/agencies/",
  func: ({ route, state }) => {
    Object.assign(state.source.data[route],{
      type: "page",
      isAgencies: true
    })  
  }
}

Can you please let me know if there is a better way to do this (best practice)?

Hi @gab.zambrano! Welcome to the community :slightly_smiling_face:

I would say the best approach is the first one you tried, but I think it wasn’t working you were missing a slash at agencies url while defining func at the beginning. So you should use this:

func: ({ state }) => {
    state.source.data['/agencies/'].isAgencies = true;    
}

Could you try this out and tell us it it works please?

5 Likes

Thanks @SantosGuillamot your solution worked perfectly!

4 Likes

Is it possible to do this while also having the custom page pull from the wordpress API still?
Essentially, I’m using ACF with Frontity and I have 3 different pages on my WP site that each have different ACF components on them, and I’d like to be able to route the pages and render a different set of components based on which page the user is on.

Yes @min, that’s possible.

You can distinguish between them using the id or the URL and create 3 different components for those pages:

<Main id="main">
  {(data.isFetching && <Loading />) ||
    (data.isArchive && <Archive />) ||
    (data.isPage && data.id === 123 && <Page1 />) ||
    (data.isPage && data.id === 456 && <Page2 />) ||
    (data.isPage && data.id === 789 && <Page3 />) ||
    (data.isPostType && <Post />) ||
    (data.is404 && <Page404 />)}
</Main>

How can I get the state in that custom component?

I tried passing it as prop
(data.isMyCustomPage && <MyCustomPage state={state} />)

And using connect: export default connect(MyCustomPage).

Also tried getting it globally window.frontity.state but did’t work.

instead of state={state}, are you trying to pass the full frontity state into that component? or just specific elements? The Frontity state variable is different from application state (the state in React). The Frontity state behaves more as a very large prop object.

That’s it! Thank you very much @min :slightly_smiling_face:

Here you can check an example of a component using the state, the FeaturedMedia component. As you could see, you just have to pass the state while defining the component and once you want to use it you don’t have to pass it as a prop anymore.

For example in your case, defining <MyCustomPage /> should look something like this:

import React from "react";
import { connect } from "frontity";

const MyCustomPage = ({ state }) => {
  ... //Your component
};

export default connect(MyCustomPage);

After that, you can use the component without passing state again: (data.isMyCustomPage && <MyCustomPage />).

Let us know if it works in your case :slightly_smiling_face: You can read a bit more about Frontity state at our docs.

1 Like

Is a bit weird. When I load the home then navigate to localhost:3000/my-custom-page everything is ok. But if I load the url directly state.source.post is an empty object.