Sunday, March 12, 2023
HomeReactPublish your e-newsletter to your Remix web site with the ConvertKit API

Publish your e-newsletter to your Remix web site with the ConvertKit API

I publish a e-newsletter known as Tiny Enhancements

My little e-newsletter is all about making small, incremental enhancements to your work and residential life. I have been publishing it for over a yr now and I’ve realized quite a bit about how one can make it higher.

It is a good way to share less-formal insights with the individuals who have given me entry to their inbox – a privilege which I do not take evenly. It additionally helps me hold my writing abilities sharp, and it is a good way to get direct suggestions on my concepts.

Though the viewers for my e-newsletter has been rising, and lately hit some milestones, I noticed that I had fairly a little bit of nice writing that went out as soon as, through e-mail, and was by no means seen once more. I wished to make it simpler for individuals to search out and skim my previous e-newsletter content material with out subscribing, as strategy to present the worth it gives. I additionally wished to benefit from the search engine marketing advantages of getting previous problems with my e-newsletter content material listed by serps.

On this submit, we’ll go over how I take advantage of the ConvertKit API to publish a e-newsletter web page for a web site constructed with Remix.

Enhance discoverability by publishing previous points

I’ve a second, necessary use case for publishing newsletters on-line. I additionally preserve my spouse’s web site – primaryfocus.television – which she makes use of to advertise her enterprise and YouTube channel (go subscribe!).

Major Focus has a e-newsletter, and I wished to make it simple for her to publish her e-newsletter to her web site as properly. Actually, I wished to offer her a strategy to get her e-newsletter on her web site with zero effort – and I used to be happy to search out that I might do this with the ConvertKit API.

Ideally, the workflow ought to appear to be this: newsletters are drafted and printed in ConvertKit, and, as soon as despatched to her subscribers, robotically printed to the web site as properly. This manner, she will be able to concentrate on writing and sending her e-newsletter, with out including an extra workflow to replace her web site.

Her web site is constructed with Remix, so this submit will go over the small print of implementation for publishing previous ConvertKit newsletters to a remix web site, utilizing their API.

Utilizing the ConvertKit API to publish newsletters to a Remix web site

Establishing an automatic e-newsletter web page is pretty easy, at the very least in idea:

  1. Write newsletters and ship them, utilizing ConvertKit’s broadcast options (for the sake of this submit, we’ll use the time period “e-newsletter” and “broadcast” considerably interchangeably).
  2. Use the ConvertKit API’s list-broadcasts endpoint to question for each printed broadcast, to populate a e-newsletter index web page
  3. For every printed broadcast, use the ConvertKit API to get the small print of that broadcast, after which render the content material of that broadcast on a web page.

To get newsletters from the ConvertKit API, I arrange 2 helper features. First is getNewsletter(), which takes a broadcast ID and returns the small print of that broadcast.

export const getNewsletter = async (broadcastId) => {

const res = await fetch(



const { broadcast: e-newsletter } = await res.json();

return e-newsletter;


The second is getAllNewsletters(), which returns a listing of all printed broadcasts. In its easiest type, this perform seems like this:

export const getAllNewsletters = async () => {

const response = await fetch(



const information = await response.json();

const { broadcasts: newsletters } = information;

return newsletters;


We’re utilizing an atmosphere variable to provide the variable CONVERTKIT_API_SECRET – it’s tremendous necessary to maintain this worth secret, and never share it on the client-side of your app.

Retaining your ConvertKit API Secret key secure on Remix websites

Remix gives a has a useful filename conference to make sure sure code solely runs serverside – any file that ends in .server.ts (for TypeScript) and .server.js (for JavaScript) will solely be run on the server. That is excellent for maintaining our ConvertKit API secret secure. I created a file known as /app/lib/util/convertkit.server.js with the 2 features above, to verify to solely ever entry the key key in there.

Rendering the e-newsletter index web page

Now that we have now a strategy to get a listing of all printed newsletters, we will use that to render a web page with a preview of every previous difficulty. I created a brand new file known as /app/routes/e-newsletter/index.jsx to render the e-newsletter index web page.

notice: I stripped out the styling/presentation code from this instance to maintain
it centered on information loading logic.

import { useLoaderData } from '@remix-run/react';

import { getAllNewsletters } from '~/lib/util/convertKit.server';

import { NewsletterListItem, NewsletterCTA } from '~/parts';

export const loader = async ({ params, request }) => {

return {

newsletters: await getAllNewsletters(),



const NewsletterListPage = () => {

const { newsletters } = useLoaderData();

return (


<h1>Learn previous newsletters</h1>

{ => (

<NewsletterListItem e-newsletter={e-newsletter} key={} />



<NewsletterCTA />




export default NewsletterListPage;

It is a pretty customary implementation, for those who’re aware of Remix. We use the loader sample to fetch the listing of newsletters, and move them to the render perform for the element with the useLoaderData() hook. We then render a listing of NewsletterListItem parts, which reveals a header picture and title for every e-newsletter, together with a hyperlink to the web page for that particular e-newsletter.

Rendering a single e-newsletter web page

Now that we have now a web page itemizing all printed newsletters, we have to create a web page template to render a single e-newsletter. I created a brand new file known as /app/routes/e-newsletter/$id.jsx to render the e-newsletter index web page (notice, as soon as once more, that presentation and styling code is eliminated right here for simplicify):

import { useLoaderData } from '@remix-run/react';

import {



} from '~/lib/util/convertKit';

import NewsletterCTA from '~/parts';

import config from '~/config';

import { getNewsletter } from '~/lib/util/convertKit.server';

export const meta = ({ information }) => {

const { e-newsletter, canonical } = information;

const publishedAt = new Date(e-newsletter.published_at);

const description = `Major focus e-newsletter: ${


}, printed on ${publishedAt.toLocaleDateString()}`;

return {


title: e-newsletter.topic

? `${e-newsletter.topic} - ${config.meta.title}`

: config.meta.title,

'og:picture': newsletterHasValidThumbnail(e-newsletter)

? e-newsletter.thumbnail_url

: null,

'og:kind': 'article',

'og:title': e-newsletter.topic,

'og:description': description,

'og:url': canonical,

'twitter:card': 'summary_large_image',



export const loader = async ({ params, request }) => {

const { id } = params;

const e-newsletter = await getNewsletter(id);

return { e-newsletter };


const NewsletterPage = () => {

const { e-newsletter } = useLoaderData();

const publishedAt = new Date(e-newsletter.published_at);

return (



<p className="publish-date">{publishedAt.toLocaleDateString()}</p>



__html: broadcastTemplateParse({ template: e-newsletter.content material }),



<NewsletterCTA />




export default NewsletterPage;

As soon as once more, we use the loader sample to question the ConvertKit API for particulars about this particular e-newsletter, and pair that with useLoaderData() to supply that data to the web page render perform. We additionally use the meta perform sample to render meta tags for search engine marketing functions – you may learn extra about that within the Remix docs.

Render the physique utilizing dangersoulySetInnerHTML

To render the contents of the e-newsletter, we’re utilizing a scary-looking React API sample – dangerouslySetInnerHTML. It is a React API that permits you to render HTML that you’ve obtained from an API, and is mostly thought-about a safety threat. Nonetheless, on this case, we’re utilizing it to render HTML that we have now generated ourselves, and we’re at the very least assured that it’s considerably secure to render. Typically talking, this can be a approach that should not be used except you might be assured that the HTML you might be rendering is secure.

Helper features for displaying ConvertKit e-newsletter content material

We’re additionally utilizing 2 helper features, that are outlined in /app/lib/util/convertKit.js (notice that these are not in convertkit.server.js, since we’d like them on the shopper facet):

import { Liquid } from 'liquidjs';

const engine = new Liquid();

export const broadcastTemplateParse = ({ template, information }) => {

const t = template.replaceAll('&quot;', '"');

const res = engine.parseAndRenderSync(t, information);

return res;


export const newsletterHasValidThumbnail = (e-newsletter) => {

const { thumbnail_url: thumbnailUrl } = e-newsletter;

if (!thumbnailUrl) return false;

if (thumbnailUrl.startsWith(''))

return false;

return true;


The newsletterHasValidThumbnail perform is a helper perform that checks if the e-newsletter has a sound thumbnail picture. ConvertKit gives a default thumbnail picture for all newsletters, however you can too add your personal, by including a picture to your e-newsletter’s physique. This perform checks if the thumbnail is the default picture, and if that’s the case, returns false so we will skip rendering it.

The broadcastTemplateParse perform is a helper perform that takes a e-newsletter template and parses it with the Liquid templating language. That is how ConvertKit permits you to customise the HTML of your e-newsletter, and use dynamic information just like the subscriber’s identify, or the date the e-newsletter was despatched.

An necessary caveat: There are heaps of little instances the place this may not work completely, because of the entire wonderful issues you are able to do with Liquid in ConvertKit. In the mean time, my e-newsletter makes use of pretty primary liquid customization – this may increasingly not do the trick for you for those who’re utilizing extra superior options. Please check your implementation completely!

Including extra element to the index web page

On the time of penning this submit, ConvertKit’s v3 API is in “lively improvement” – and as such, it is possible not characteristic full. For instance, the present API for list-broadcasts gives a minimal quantity of information about every broadcast:



"broadcasts": [


"id": 1,

"created_at": "2014-02-13T21:45:16.000Z",

"subject": "Welcome to my Newsletter!"



"id": 2,

"created_at": "2014-02-20T11:40:11.000Z",

"subject": "Check out my latest blog posts!"



"id": 3,

"created_at": "2014-02-29T08:21:18.000Z",

"subject": "How to get my free masterclass"



Since we need to create an index web page that reveals a thumbnail for every e-newsletter, we have to question the API for extra details about every e-newsletter. In the mean time, the easiest way to do that is to question the API for every e-newsletter individually. It isn’t superb, since we have to ship an API name for every e-newsletter proven on the index web page, however I am hoping that with a little bit of suggestions, ConvertKit will add extra information to the list-broadcasts API endpoint.

We have already got a perform to question the API for a single e-newsletter (keep in mind getNewsletter from earlier?), so we will use that to question the API for every e-newsletter on the index web page.

Filter out un-published newsletters

The list-broadcasts endpoint additionally does not at the moment return whether or not or not a given broadcast was printed but – which signifies that for those who draft a future broadcast in your ConvertKit dashboard, it would nonetheless be returned by the listing API. I do not need to share these yet-to-be-sent newsletters on any of my websites, so we’ll add some logic to getAllNewsletters to filter out any newsletters that have not been printed but.

Do not render newsletters with duplicate topic strains

There are some instances the place I ship the identical e-newsletter a number of occasions, with the identical topic line, to totally different audiences – often as a result of I’ve forgotten to incorporate some particulars within the authentic broadcast. This leads to a number of broadcasts with basically the identical content material. I do not need to present these on my web site, so I added some logic to deduple any newsletters which have the identical topic line, displaying solely the newest one (which ought to be essentially the most right, in idea).

That is the up to date code for getAllNewsletters in convertKit.server.js:

export const getAllNewsletters = async () => {

const response = await fetch(



const information = await response.json();

const { broadcasts } = information;

const newsletters = await Promise.all( => getNewsletter(


const dedupedBySubject = {};


.filter((e-newsletter) => !!e-newsletter.published_at)

.kind((a, b) => new Date(b.published_at) - new Date(a.published_at))

.forEach((e-newsletter) => {

if (!dedupedBySubject[newsletter.subject])

dedupedBySubject[newsletter.subject] = e-newsletter;


let nls = [];

for (let topic in dedupedBySubject) {



return nls;


And with that, our journey is full – we have created a /e-newsletter web page that reveals a listing of all of our newsletters, with a thumbnail picture for every one. We have additionally added a /e-newsletter/$id web page that reveals the complete content material of a given e-newsletter.


I hope this submit has been useful to you, and that you’ve got realized one thing new about how one can use ConvertKit’s API to create a customized e-newsletter index web page in your Remix web site. To see what this seems like in observe, head over to primaryfocus.television/e-newsletter.

In my subsequent submit, I am going to share how I used an analogous implementation in Subsequent.js on my web site –

When you have any questions, or for those who’ve discovered a bug in my code, please be at liberty to succeed in out to me on Twitter @irreverentmike!



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments