Create e-mail subscription panel with Refine and Strapi.
Tags: typescript, strapi, react, refine
Published: 2021-10-25 03:32:00

---
canonical_url: https://refine.dev/blog/e-mail-subscription-panel-with-refine/
---
 We will make a web application that allows you to quickly create subscribers and send emails to your subscribers in a simple way. We’ll use refine to develop the frontend easily and strapi for backend solutions.

Let's start by creating our strapi and refine projects.

## Creating API with `Strapi`
```bash
npx create-strapi-app strapi-email-subscription-api --quickstart
```

After the project is loaded, the admin panel will open automatically open in the browser. We need to create an admin user in order to log in to the strapi.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tholw0tcd57usv17dzp1.png)

With the information we will create here, we can now enter the strapi and start shaping our backend.

After logging into the Strapi interface, we have two collection models that we need to create for our e-mail subscription project.

We will create these collections from the Collection-Types Builder part of the strapi.

> Subscribers
* `name` text field
* `email` Email

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9binctvmlznn9dhb0o4g.png)
 
> Messages
* `subject` text field
* `text` text field

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1fd69vuymp2cvsat1p6.png)
 

With these collections and features we have created, we can now create subscribers, delete them and make changes to them.

## Creating panel with `refine`

Now let's refine the subscription panel. With [superplate](https://github.com/pankod/superplate), we can quickly create a refine project

```bash
npx superplate-cli email-subscription-panel
```

Select the following options to complete the CLI wizard:

```bash
? Select your project type:
> refine

? Package manager:
> Npm

? Do you want to customize the theme?:
> No (Ant Design default theme)

? Data Provider: 
> Strapi

? Do you want to customize layout?:
> Yes, I want

? i18n - Internationalization: 
> No
```

After the upload is finished, let's go into our project and see how it looks.

```bash
cd email-subscription-panel
npm run dev
```

This is a example Refine project:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r83edwwho1qc8htmm4ra.png)

Let's list our messages and subscribers with refine. Here are the changes we need to make:

* Change Strapi API URL from refine
* Adding resources according to the collection name we created in Strapi

/App.tsx

```tsx
import { Refine, Resource } from "@pankod/refine";

import "@pankod/refine/dist/styles.min.css";
import { DataProvider } from "@pankod/refine-strapi";
import strapiAuthProvider from "authProvider";
import { Header, Layout, OffLayoutArea } from "components";


function App() {
 - const API_URL = "your-strapi-api-url";
 + const API_URL = "http://localhost:1337";

  const { authProvider, axiosInstance } = strapiAuthProvider(API_URL);
  const dataProvider = DataProvider(API_URL, axiosInstance);
  return (
    <Refine
      dataProvider={dataProvider}
      authProvider={authProvider}
      Header={Header}
      Layout={Layout}
      OffLayoutArea={OffLayoutArea}
    >
      <Resource
        name="subscribers"/>

      <Resource
        name="messages"/>
    </Refine>
  );
}

export default App;

```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvyvumjcyo37moyowakc.png)

After adding the resources, we need to define a user in the strapi in order to be able to login to the refine. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uk3yr1xpyzjkib3lxqq.png)

Let's login with this user we created

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pu3zn1pp2293maiug1os.gif)
 
We can now list subscribers and messages and make changes to our list. Before doing this, let's create test users and messages on the strapi side. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqrrs73888u5m9yb4m5j.png)
 


Create SubscriberList.tsx and MessagesList.tsx file under the pages folder. Then, let's create our component as follows with the components and hooks that come with refine.

/src/pages/subscriber/SubscriberList.tsx

```tsx
import React from "react";
import {
  useTable,
  List,
  Table,
  DateField,
  DeleteButton,
  IResourceComponentsProps,
} from "@pankod/refine";

import { ISubscriber } from "interfaces";

export const SubscriberList: React.FC<IResourceComponentsProps> = () => {
  const { tableProps } = useTable<ISubscriber>();
  return (
    <List>
      <Table {...tableProps} rowKey="id">
        <Table.Column dataIndex="id" title="Id" />
        <Table.Column dataIndex="name" title="Name" />
        <Table.Column dataIndex="email" title="E-mail" />
        <Table.Column
          dataIndex="created_at"
          title="createdAt"
          render={(value) => <DateField format="LLL" value={value} />}
        />
        <Table.Column<ISubscriber>
          title="Unsubscribe"
          dataIndex="actions"
          render={(_, record): React.ReactNode => {
            return (
              <DeleteButton size="small" recordItemId={record.id} hideText />
            );
          }}
        />
      </Table>
    </List>
  );
};

```

/src/pages/mail/MessageList.tsx

```tsx
import React from "react";
import {
  useTable,
  List,
  Table,
  DateField,
  IResourceComponentsProps,
} from "@pankod/refine";

import { IMail } from "interfaces";

export const MessageList: React.FC<IResourceComponentsProps> = () => {
  const { tableProps } = useTable<IMail>();
  return (
    <List>
      <Table {...tableProps} rowKey="id">
        <Table.Column dataIndex="id" title="Id" />
        <Table.Column dataIndex="subject" title="Subject" />
        <Table.Column dataIndex="text" title="Body" />
        <Table.Column 
          dataIndex="created_at"
          title="createdAt"
          render={(value) => <DateField format="LLL" value={value} />}
        />
      </Table>
    </List>
  );
};
```

/src/interfaces/intex.d.ts

```tsx

export interface ISubscriber {
  id: any;
  name: string;
  email: string;
  created_at: string;
}

export interface IMail {
  subject: string;
  text: string;
  to: string;
  create_at: string;
}
```

In this component:

We used refine's [list](https://refine.dev/docs/examples/list/useSimpleList) and [table](https://refine.dev/docs/api-references/hooks/table/useTable/) to show our subscribers and messages.

Now let's see how our subscriber panel looks like:

`Subscriber:`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khuhh8uqfatu7gfs9ekq.png)


`Messages:`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apscx8lqsigc9czrszgm.png)


As you can see, we were able to list our subscribers and e-mails very simply with refine. Now let's examine how we can create subscribers and messages from our interface.

/src/pages/subscriber/create.tsx

```tsx
import {
  Create,
  Form,
  Input,
  useForm,
  IResourceComponentsProps,
} from "@pankod/refine";

import { ICreateSubscriber } from "interfaces";

export const CreateSubscriber: React.FC<IResourceComponentsProps> = () => {
  const { formProps, saveButtonProps } = useForm<ICreateSubscriber>();

  return (
    <Create saveButtonProps={saveButtonProps}>
      <Form {...formProps} layout="vertical">
        <Form.Item label="Name" name="name">
          <Input />
        </Form.Item>
        <Form.Item
          label="E-mail"
          name="email"
          rules={[
            {
              required: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
      </Form>
    </Create>
  );
};
```
  
/src/pages/mail/create.tsx

```tsx
import React, { useState } from "react";
import {
  Create,
  Form,
  Input,
  useForm,
  IResourceComponentsProps,
} from "@pankod/refine";

import ReactMarkdown from "react-markdown";
import ReactMde from "react-mde";
import "react-mde/lib/styles/css/react-mde-all.css";
import { IMail } from "interfaces";

export const MailCreate: React.FC<IResourceComponentsProps> = () => {
  const { formProps, saveButtonProps } = useForm<IMail>();
  const [selectedTab, setSelectedTab] = useState<"write" | "preview">("write");

  return (
    <Create saveButtonProps={saveButtonProps}>
      {console.log("create")}
      <Form {...formProps} layout="vertical">
        <Form.Item
          label="Subject"
          name="subject"
          rules={[
            {
              required: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Body"
          name="text"
          rules={[
            {
              required: true,
            },
          ]}
        >
          <ReactMde
            selectedTab={selectedTab}
            onTabChange={setSelectedTab}
            generateMarkdownPreview={(markdown: any) =>
              Promise.resolve(<ReactMarkdown>{markdown}</ReactMarkdown>)
            }
          />
        </Form.Item>
        <Form.Item
          label="To"
          name="to"
          rules={[
            {
              required: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
      </Form>
    </Create>
  );
};
```

Using refine's [form](https://refine.dev/docs/api-references/hooks/form/useForm) and [create](https://refine.dev/docs/api-references/components/basic-views/create) components, we can now create subscribers and messages with refine.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adubsn6wjo35t5zdz0fn.gif)
  

We're done with our panel. We can now list, create and delete subscribers. Finally, the step of sending real mails using our panel and strapi is left. Let's see how we do this.


# Strapi E-mail Plugin

In order to send mail via Strapi, we need to install the [strapi-email](https://strapi.io/documentation/developer-docs/latest/development/plugins/email.html#programmatic-usage) plugin in our api project that we created above.

Let's open our API project that we created and download the email plugin.

```bash
cd strapi-email-subscription-api
npm install strapi-provider-email-sendgrid --save
```

After installing your plugin you will need to add some settings in config/plugins.js. If this file doesn't exists, you'll need to create it. 

#### Configure your provider

`Path — ./config/plugins.js`

```js
module.exports = ({ env }) => ({
    email: {
      provider: 'sendgrid',
      providerOptions: {
        apiKey: env('SENDGRID_API_KEY'),
      },
      settings: {
        defaultFrom: 'your-email-adress',
        defaultReplyTo: 'your-email-adress',
        testAddress: 'your-email-adress',
      },
    },
  });
```

 **💡 TIP**: Strapi sends emails via sendgrid. That's why you need to create a [SendGrid](https://sendgrid.com/) account and get an api-key.


Now, let's send the text and subject in the collection of messages we created over the strapi as parameters to the send() function of the email plugin.

`api/messages/controllers/messages.js`

```js
const { parseMultipartData, sanitizeEntity } = require("strapi-utils");

module.exports = {
  async create(ctx) {
    let entity;
    if (ctx.is("multipart")) {
      const { data, files } = parseMultipartData(ctx);
      entity = await strapi.services.messages.create(data, { files });
    } else {
      entity = await strapi.services.messages.create(ctx.request.body);
    }

    entity = sanitizeEntity(entity, { model: strapi.models.messages });

    const { subject, text } = entity;

    const worker = (await strapi.services.subscribers.find()).map(
      (subscriber) => {
        let to = subscriber.email;

        return strapi.plugins["email"].services.email.send({
          subject,
          text,
          to,
        });
      }
    );

    await Promise.all(worker);

    return entity;
  },
};
```

 Our project is finished. Let's try it now.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gk1ot7nc2vcfzjj3tlw6.png)
 
Let's send the same e-mail to our subscribers shown in the picture at once.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vch4eulz101yzsnyujoj.gif)
 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elob8kppgposhf0mgg6p.png)

Sending mail was successful. As you can see, we were able to send the same email to all subscribers by sending a single email.

[Here is repo](https://github.com/pankod/refine/tree/master/examples/blog/mailSubscription) 

For more information about Refine: [https://refine.dev/](https://refine.dev/)