How to Schedule a Meeting in Google Meet with Apps Script

This Apps Script sample shows how you can programmatically schedule video meetings inside Google Meet with one or more participants using the Google Calendar API. It can be useful for teachers who wish to schedule regular meetings with their students but instead of manually creating meeting invites, they can easily automate the whole process for the entire class.

Schedule Google Meeting

Setup Google Meeting with Apps Script

Give your meeting a title, the start date, the meeting duration, the list of attendees and how often you wanted to be reminded of the upcoming Google meeting. A new meeting event will be added to your Google Calendar and you’ll also be provided with a Google Meet link that you share with your students and colleagues through mail merge.

const createGoogleMeeting = () => {
  // The default calendar where this meeting should be created
  const calendarId = 'primary';

  // Schedule a meeting for May 30, 2022 at 1:45 PM
  // January = 0, February = 1, March = 2, and so on
  const eventStartDate = new Date(2022, 5, 30, 13, 45);

  // Set the meeting duration to 45 minutes
  const eventEndDate = new Date(eventStartDate.getTime());
  eventEndDate.setMinutes(eventEndDate.getMinutes() + 45);

  const getEventDate = (eventDate) => {
    // Dates are computed as per the script's default timezone
    const timeZone = Session.getScriptTimeZone();

    // Format the datetime in `full-date T full-time` format
    return {
      timeZone,
      dateTime: Utilities.formatDate(eventDate, timeZone, "yyyy-MM-dd'T'HH:mm:ss"),
    };
  };

  // Email addresses and names (optional) of meeting attendees
  const meetingAttendees = [
    {
      displayName: 'Amit Agarwal',
      email: 'amit@labnol.org',
      responseStatus: 'accepted',
    },
    { email: 'student1@school.edu', responseStatus: 'needsAction' },
    { email: 'student2@school.edu', responseStatus: 'needsAction' },
    {
      displayName: 'Angus McDonald',
      email: 'assistant@school.edu',
      responseStatus: 'tentative',
    },
  ];

  // Generate a random id
  const meetingRequestId = Utilities.getUuid();

  // Send an email reminder a day prior to the meeting and also
  // browser notifications15 minutes before the event start time
  const meetingReminders = [
    {
      method: 'email',
      minutes: 24 * 60,
    },
    {
      method: 'popup',
      minutes: 15,
    },
  ];

  const { hangoutLink, htmlLink } = Calendar.Events.insert(
    {
      summary: 'Maths 101: Trigonometry Lecture',
      description: 'Analyzing the graphs of Trigonometric Functions',
      location: '10 Hanover Square, NY 10005',
      attendees: meetingAttendees,
      conferenceData: {
        createRequest: {
          requestId: meetingRequestId,
          conferenceSolutionKey: {
            type: 'hangoutsMeet',
          },
        },
      },
      start: getEventDate(eventStartDate),
      end: getEventDate(eventEndDate),
      guestsCanInviteOthers: false,
      guestsCanModify: false,
      status: 'confirmed',
      reminders: {
        useDefault: false,
        overrides: meetingReminders,
      },
    },
    calendarId,
    { conferenceDataVersion: 1 }
  );

  Logger.log('Launch meeting in Google Meet: %s', hangoutLink);
  Logger.log('Open event inside Google Calendar: %s', htmlLink);
};

Also see: Generate Add to Calendar Links

Google Meeting with Recurring Schedule

The above code can be extended to create meetings that occur on a recurring schedule.

You need to simply add a recurrence attribute to the meeting event resource that specifies the recurring event in RRULE notation. For instance, the following rule will schedule a recurring video meeting for your Maths lecture every week on Monday, Thursday for 8 times.

{
  ...event,
  recurrence: ["RRULE:FREQ=WEEKLY;COUNT=8;INTERVAL=1;WKST=MO;BYDAY=MO,TH"];
}

Here are some other useful RRULE examples:

  • FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR - Occurs every week except on weekends
  • FREQ=MONTHLY;INTERVAL=2;BYDAY=TU - Occurs every Tuesday, every other month
  • INTERVAL=2;FREQ=WEEKLY - Occurs every other week
  • FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH;BYMONTH=12 - Occurs every other week in December on Tuesday and Thursday
  • FREQ=MONTHLY;INTERVAL=2;BYDAY=1SU,-1SU - Occurs every other month on the first and last Sunday of the month

What is the answer here

Develop a visual basic program that asks the user to enter an integer positive number, then reverse this number as a single integer and display it in a textbox. For example, if the user enters 235 the output should be 532. If the input is 79534 the output is 43597 (this means the input can accept any number of digits).

The Automattic Theme Team Announces Blockbase, Its New Block Parent Theme

Viewing the Blockbase starter WordPress theme in the upcoming site editor.
Blockbase WordPress theme as seen from the site editor.

Any WordPress company that builds and maintains themes worth its salt is already doing at least some preliminary work as WordPress inches ever closer to bundling its upcoming block theme system. Automattic’s Theme Team is no exception. Ben Dwyer announced the team’s new Blockbase parent theme on the Theme Shaper blog yesterday. It includes support for global styles and block templating.

The theme is based on the classic, block-editor-ready Blank Canvas project that Automattic launched in January. Until a few days ago, it was even named Blank Canvas Blocks. I have been checking in on the team’s work in the past couple of months, waiting to see how Blockbase and Mayland Blocks, a child theme based on the team’s original Mayland, were coming along.

The team has built a well-rounded system that should keep them from overhauling each block theme they create for WordPress.com every time there is a change. For theme authors who have yet to dip their toes into the block-theme pond, it might also be the starting point they need.

The Holy Grail of some block theme designers will be creating a project with little more than a theme.json file. The goal: let WordPress generate all the CSS via setting and style configurations. It will never be possible for all theme authors to achieve their design goals through this — most will need at least some custom CSS. And, whether such a goal is A Good Thing remains to be seen.

Nevertheless, the future of theme design will lean on JSON-configured and generated styles. Blockbase leverages this system to create a standardized set of guideposts for its eventual child themes. This ensures that creators are not continually updating their themes as block styling rapidly evolves.

Blockbase serves as a bridge between what is currently possible to configure via a theme.json file and what will be possible in the future.

It is the modern-day Underscores (_s) for blocks, and the WordPress theme design community will need such a project moving forward. They will need a starting point and educational tool, and Blockbase is just that.

The latest version of the Gutenberg plugin only covers a fraction of what it and, eventually, WordPress will handle in the coming months and years. However, it does allow theme authors to add custom settings via the settings.custom key, creating their own CSS variables that are automatically output in the site’s head. The Theme Team used this feature to their advantage. Where Gutenberg does not currently support a setting, Blockbase has a custom theme.json parameter. The theme then uses the generated CSS variables in its stylesheet. As Gutenberg and WordPress eventually support more of these settings out of the box, the team can simply remove unneeded code.

The theme is not altogether unopinionated. “Blockbase is intended to be a representation of all the theme style settings that we believe should eventually live in Global Styles and be configurable by users,” wrote Dwyer. Block theme development could still change in unexpected ways as the system continues to be refined, and the theme will need to change along with it. Bumps in the road should be expected.

It is a project that soon-to-be and current block themers can learn from. While it is currently available via GitHub, Dwyer said the team can investigate making it a npm package if there is enough interest.

One of the most unique things the theme does is work with the theme.json settings via PHP. In Blockbase’s functions.php file, it plucks out font-family names registered in the JSON file. It then automatically loads their associated stylesheets from the Google Fonts API. While it is not documented in the code (it should be), I am taking an educated guess that the goal is to allow child theme authors to declare fonts in their own theme.json files and for Blockbase to handle the loading.

The code is relatively simple. It is the technique that matters.

As developers grapple with the changing nature of WordPress theme architecture, they will need to find new solutions for some of the old problems they previously solved in a PHP-only world. With most theme configuration moving to JSON and templates to HTML, it can be easy to feel like they are losing that dynamic nature of PHP. It was something reliable, and developers have spent years honing custom systems around the old WordPress.

The new WordPress delivers a more robust set of design tools out of the box, but it can seem a bit foreign. That is why seeing real-world code examples of how others are handling these features is a vital step in transitioning more theme authors to block-based development.

This may be a small code sampling, but I am impressed by the clever thinking. In my journey to learn more about how block themes work, I often find myself hitting a brick wall, fighting against the system. This code works with it instead of against it.

I do not often write several paragraphs about one function that spans a mere 21 lines of code, but I am still a developer at heart and love seeing elegantly simple solutions. It is not about the font-loading technique; it is about solving problems. This code will undoubtedly be copied many thousands of times over in the coming years and modified for many more thousands of uses. If you are a theme developer and do not see it yet, you will.

WhoGoHost Review

WhoGoHost is a Nigerian web hosting provider. It offers a variety of services, from dedicated cloud hosting to hosting a WordPress site. It’s fair to say that both beginners and experts are the service’s target audience, with varying success levels. It’s not going to be a web hosting provider we recommend at the top of the pile, but it does provide responsive customer support and several easy-to-use services.

WhoGoHost Pros and Cons

Pros

  • Easy to use
  • Responsive customer support
  • Affordable

Cons

  • Not very reliable
  • Uncertain uptime
Compare The Best Web Hosting Providers
We’ve reviewed dozens of web hosting providers and narrowed them down to the best options.
See Top Picks

How WhoGoHost Compares to the Best Web Hosting Providers

WhoGoHost isn’t a contender for the best web hosting provider. Unfortunately, it’s let down by unstable uptimes and a general sense of being unreliable and inconsistent. However, the plans offered are affordable, the customer support is responsive, and some of its one-click services are easy to use.

Two better alternatives to WhoGoHost would be Bluehost and DreamHost. Bluehost offers excellent web hosting solutions at competitive prices. It also provides 24/7 customer support through phone and live chat and powerful SEO functionality that allows you to grow with them. DreamHost is highly affordable too, and it offers a completely custom control panel with a full 97-day money-back guarantee.

WhoGoHost Site Uptime

Site uptime is an essential part of web hosting. Almost all companies need a reliable service to ensure their visitors can reach their site and navigate it without problems. WhoGoHost promises its customers a 99.9% network uptime—in fact, it guarantees it. It’s worth mentioning here that many web host providers offer this “guarantee,” but that doesn’t necessarily mean you’re going to get that service. That’s true even if you go for dedicated over shared hosting.

Some customers with WhoGoHost have complained that their sites have been down, sometimes inexplicably, and that WhoGoHost, although ready to help and respond, wasn’t easily able to solve their issue. This isn’t a minor problem, so customers should be wary of this before choosing WhoGoHost as their web provider.

With that said, WhoGoHost has generally improved its uptime over the last few years, and it’s clear it’s aware of its site uptime issues. It’s also fair to say that many web providers do occasionally fall short in this area. I’d still be somewhat cautious here, though, as even new bloggers expect a site to be reachable at almost all times.

WhoGoHost Customer Support

WhoGoHost does far better when it comes to customer support. Good customer support breaks down into two sections: how quickly a company can respond and how effective it is at solving the problem. WhoGoHost is a responsive company, and it does engage with its customers frequently. It also offers a pretty good turnaround time on responses.

It provides customer support through quite a few different forms, including live chat, WhatsApp, a direct call to them 24/7, or an open support ticket. It also offers an address on its site, which is always good to see. This is an encouraging look overall.

However, WhoGoHost doesn’t do as well at that second part of what I consider customer support: it’s not always that effective at sorting out customers’ problems. Customers need to have their issues sorted and solved quickly and efficiently, full stop, and WhoGoHost just doesn’t have the best track record here. There is definitely a clear message here that WhoGoHost is active and listening, and it has successfully sorted problems out more often than not, but this still isn’t the ideal customer service experience.

WhoGoHost Price

When it comes to price, WhoGoHost is something of a winner. Not only are its prices highly affordable, as you can see below, but they’re also generally clearly presented. A customer should know, in theory, what they’re getting from a price page alone, and WhoGoHost’s pricing page does this well.

We have five overall tiers for web hosting:

  • Aspire / $1.40 monthly / 10GB Bandwidth / 2GB Webspace
  • Premium / $1.95 monthly / 30GB Bandwidth / 6GB Webspace
  • Pro / $2.65 monthly / 45GB Bandwidth / 12GB Webspace
  • Deluxe / $5.86 monthly / Unlimited Bandwidth / 25GB Webspace

Each of these tiers offers more features. For example, on the Aspire tier, you’re looking at just 2GB of webspace, 10GB of bandwidth, and four subdomains. This goes up to 12GB of webspace, 45GB of bandwidth, and 15 subdomains on the Pro level. It’s of course worth noting that having a limited bandwidth on a website isn’t something you’ll want to stick with forever, certainly as traffic and the complexity of your site increase. There’s also something to be said for prices that look too good to be true, and most hosting providers average between $10-$20 a month.

That said, this shouldn’t instantly exclude you from choosing WhoGoHost—instead, I would encourage you to look around at all the hosting options and then compare them before deciding. There have been happy customers with WhoGoHost, but always keep these things in mind to avoid potential frustration down the line.

WhoGoHost Site Backups

Few things are as crucial as having site backups. At some point, almost all companies will experience trouble, either in the form of data loss or via cyber attack. Site backups ensure that if the worst happens, you’ll be able to restore a site to a working version before disaster struck. It goes without saying how much stress and panic this can prevent.

WhoGoHost offers site backups through the form of CodeGuard Website Backup. CodeGuard is a well-respected site backup and monitoring service, so this is indeed a good thing. For example, CodeGuard is used by top competitors such as Bluehost. You’ll be getting top-notch service from CodeGuard and can be safe in the knowledge that your site will be both protected and backed up.

There are a couple of additional points here: there are services, such as plugins on WordPress, that provide site backups for free. While these might not offer all of the most comprehensive features, it’s certainly a good option if you don’t want to pay for backups.

CodeGuard itself is provided through WhoGoHost based on the amount of backup storage you need. So, for instance, 10GB of backup storage will cost around $7.14 a month. In comparison, larger amounts such as 100GB can reach much higher prices, about $32.13 a month. For that, you’ll have access to daily automated site backups, file change monitoring, and more.

How much storage you opt for here is, of course, up to you, although it might be worth considering the free options before diving in with this extra service. After all, it’s an additional monthly cost on top of everything else.

WhoGoHost WordPress Hosting

WordPress hosting is a popular choice for obvious reasons: it’s one of the best platforms to build a site and features access to powerful plugins and beautiful themes. Web hosting providers realize this and now offer dedicated WordPress hosting. This entails having WordPress preinstalled, and most web hosts offer a one-click install here, allowing you to get up and running without fuss.

WhoGoHost offers WordPress hosting via either monthly or annual payments. It comes in three tiers: WP Beginner, WP Standard, and WP Standard Plus. We see the key difference between these levels in the amount of SSD storage available, the bandwidth, and the number of additional websites you can have as part of the package. On all levels, you get a free SSL certificate—this permits encrypted communication between a web browser and a server, so it’s great to have. In fact, not only will visitors expect to see an SSL certificate, but search engines also don’t look too kindly on sites without one.

All plans also come with several standard features, including free hosting transfers, 24/7 support, a free domain name with the annual plan, and cPanel Access. A cPanel is essentially a control panel that simplifies site and server management.

So, this is a good offering here, and although now standard among web hosts, it’s something that I’m pleased to see WhoGoHost provide. Of course, the actual test relies on the hosting’s stability, and WhoGoHost doesn’t have the best record here. If in doubt, it’s worth remembering that you can get a refund if you request cancellation within 30 days of purchase. That will help to mitigate any potential risk here.

WhoGoHost Email Hosting

Email hosting allows companies to utilize email servers. In other words, if you own a domain and want that email service with that domain name, you would have to purchase email hosting. The good news is that email hosting is usually inexpensive—but it requires you to have a hosting plan before buying the extra functionality. For example, Bluehost offers this with its hosting plans starting at $2.95 a month.

WhoGoHost’s email hosting comes in four packages: BE Level 1, 2, 3, and 4. The primary difference between the levels is the number of email accounts, and we’re looking at a low starting price of $1.40 a month. This is cheaper than some competitors, although you have to factor in the hosting quality you receive. You also have the option of paying for this annually, which is preferred by some businesses. There is no refund available if you cancel your email hosting account—but this isn’t a critical factor here, considering the overall affordability.

You may be wondering why you’d get email hosting. There are quite a few reasons, but centrally it’s the professional look this offers an individual or company. Seeing emails with a domain name in them is a good way of legitimizing a business, and it’s often looked upon favorably by customers. They are also secure platforms, with generally a good level of storage space, and they’re usually effective at blocking out spam and viruses from infiltrating a network.

This is a cheap email hosting solution: WhoGoHost does well in this area.

WhoGoHost Site Builder

WhoGoHost offers a site builder if you’d prefer an even easier way of getting your site online. This is an option for those that fear coding and want to jump straight in and get to work. I was impressed to see this comes with a 30-day free trial, which is a nice bonus to have. Effectively a good site builder will allow a user to drag and drop and piece together their site. These are now commonly offered along with the hosting part of the service.

WhoGoHost’s site builder offers the expected drag and drop functionality that other hosts provide, along with custom templates and a framework that works across desktop and mobile. Meaning: you can quickly build it for multiple devices, too—an increasingly important part of an online strategy.

The site builder comes in three different plans, and the changes between them tend to focus on the disk space or web space, the bandwidth, and whether you can build an ecommerce store on that framework. These starting prices are low—beginning at $3.63/month for WhoGoHost’s Lite Plan—and are generally competitive with others.

Whether you decide to use a site builder does come down to your knowledge and the time available to build a website. I think a site always tends to look better when WordPress is involved in the process, but some great site builder options are available. WhoGoHost’s site builder is fair, but it will be using the same hosting to power this, so keep that in mind. There’s no refund if you cancel your site builder account either.

Compare The Best Web Hosting Providers
We’ve reviewed dozens of web hosting providers and narrowed them down to the best options.
See Top Picks

Summary

WhoGoHost is an affordable web hosting provider that offers a large variety of services. We wouldn’t naturally gravitate towards recommending it, as there are so many great alternatives out there and WhoGoHost doesn’t quite measure up to many of them in certain areas. However, the hosting company is trying to improve its overall offering, making it perhaps one to watch in the future. The customer service support is decent, and some customers have mentioned they found WhoGoHost easy to use across the packages. For now, we’d still happily recommend Bluehost and DreamHost if you’re looking for super reliable web hosts and don’t want to take a chance.

Spotify Refines Third Party Application Development Process

Spotify has announced several changes to the company’s developer platform that are intended to help streamline the process of building third-party applications that leverage the media company’s resources. These updates include two new development modes and a significant update to the Spotify Developer Policy. 

IncFile Review

Incfile is one of the most popular business formation services on the web. It’s a fast and easy way for anyone to start a business without the hassle and costs associated with hiring an attorney.

Over half a million businesses have launched with Incfile since 2004.

As a pioneer in the business formation industry, Incfile uses modern technology and provides exceptional customer service to entrepreneurs who need assistance in registering a new business.

This service provider is best known for its rock-bottom prices. In fact, you use Incfile to form your business for free (more on that later).

Not sure if Incfile is right for you? Fortunately, you’ve come to the right place. We’ll break down the service offerings, prices, packages, and everything else you need to know before making a decision. You’ll even learn more about what real customers are saying about Incfile.

Ready to get started? Sign up with Incfile today.

Incfile Business Formation Services

Incfile’s business formation services can be segmented into three main categories—LLC services, incorporation services, and registered agent services. We’ll cover each of these in greater detail below.

Incfile LLC Services

When it comes to forming a limited liability company (LLC) with Incfile, simplicity is the first thing that comes to mind. In just a handful of simple steps, they walk you through the entire process online.

Whether you’re looking for just the basics to get you started or a customized LLC kit, Incfile has you covered.

Every LLC package from Incfile comes with the following:

  • Unlimited company name availability searches
  • Articles preparation and filing
  • Next-business-day processing
  • Registered agent services (free for one year)

You can get all of these services from Incfile for free at the lowest pricing plan; just pay the state fees. This simple and inexpensive setup is why so many entrepreneurs turn to Incfile for LLC formation services.

 

There are three LLC packages for you to choose from:

  • Silver — $0 + state fee
  • Gold — $149 + state fee
  • Platinum — $299 + state fee

If you’re on a tight budget and just want the basic services from the bullet list above, you can get that Silver package without paying a dime to Incfile. As previously mentioned, this even comes with a free year of registered agent services.

With that said, the Silver package doesn’t come with an employer identification/tax ID (EIN). This is definitely something you’ll need, so it makes sense to get it directly from Incfile. You can add that to your Silver package for $70.

Alternatively, the Gold plan comes with several additional premium services. This includes an EIN, preparation and filing of IRS form 2553, an operating agreement, banking resolution, express shipping, and a business formation kit.

The Platinum package comes with added perks like business contract templates, free domain name, email service, expedited filing, and a better version of the business formation kit.

Not all of the Platinum features are really necessary, especially the free domain and email service. These are things you should be getting elsewhere.

The state fee varies depending on your location. It’s easy to see those exact figures directly on Incfile’s website when you select your state. All of the pricing is straightforward and transparent.

Incfile can also provide you with assistance for trademarks, a Certificate of Good Standing, DBA (doing business as) names, and business license research.

The fact that you can use Incfile to register your LLC for free is probably the best value you’ll find on the web. With that said, I’d recommend the Gold package, as it truly comes with everything you’ll need to form your LLC, including an EIN.

Incfile Incorporation Services

Incfile also has exceptional incorporation services. You can use them to form a C-Corporation, S-Corporation, or a nonprofit corporation.

Like the LLC services, the entry-level Silver package from Incfile is 100% free for the incorporation services as well. So regardless of the entity type you need to form, all you’ll have to pay is the state fee (prices vary by state).

The free plan comes with preparation and filing for the articles of incorporation and unlimited name availability searches.

You’ll also get registered agent services free for the first year.

With that said, the free plan does lack some of the services you’ll need to officially incorporate. The Gold package, which costs $149 plus state fees, includes an EIN, IRS Form 2553, corporate bylaws, and more.

The Gold package also comes with a business formation kit and express shipping. So it’s really a great value. Incfile has a Platinum package ($299 + state fees). This comes with expedited filing, faster shipping, a free domain, and email service.

I also like Incfile’s incorporation services because they have so much information about each entity type. Even if you’ve never formed a C-corp, S-corp, or nonprofit, you can basically learn everything you need directly from Incfile’s website—for free.

Furthermore, there’s a dedicated learning center with research tools, guides, and seemingly endless resources for incorporations.

If you’re launching a C-Corp, S-Corp, or nonprofit, Incfile is a cost-effective alternative to hiring a traditional lawyer to handle this for you.

Incfile Registered Agent Services

Incfile’s registered agent services are available in all 50 states. They’ll help you manage crucial legal documents and prevent errors with your filings.

Every LLC or corporation is required to have a registered agent. So if you’re using Incfile’s incorporation services, it makes sense to use their registered agents as well. However, existing businesses can also take advantage of registered agent services from Incfile. They make it easy for you to change registered agents, regardless of the state you’re operating in.

Here’s a quick overview of the services and benefits you’ll get from an Incfile registered agent:

  • Always available during business hours
  • Available to receive state and IRS mail correspondence
  • Available to receive documents and legal proceedings on behalf of your LLC or corporation
  • Email and SMS alerts whenever documents are received
  • Automatic mail forwarding for legal correspondence, documents, and other information

My favorite part of Incfile’s registered agent service is the online dashboard.

You can manage everything you need online, at a glance, from anywhere with Internet access.

If you’re using Incfile to form an LLC, S-Corp, C-Corp, or nonprofit, the registered agent services are free for the first year. Then the rate renews at the regular price of $119 per year thereafter.

Customers rave about Incfile’s registered agent services. After reading through testimonials and reviews on their website and third-party platforms, it seems like the registered agent service is why business owners trust Incfile for the long haul.

While it’s obviously great to have the free registered agent initially, so many customers mention how they have been using Incfile for years—long after the complimentary year has expired. So when businesses have the chance to look elsewhere after that first year, they continue to stick with Incfile, which is extremely encouraging.

From helping you avoid missed deadlines, fines, and non-compliance issues to managing paperwork and responding quickly in the event of a lawsuit, Incfile’s registered agent services are top-notch.

This gives you the opportunity to focus on your business while they handle the little things. If something important arises, you’ll be notified by SMS or email immediately.

Overall Pricing and Value

We’ve mentioned Incfile’s prices throughout this guide. But I want to take a moment to provide you with a more in-depth explanation of the packages and rates.

Whether you’re forming an LLC or corporation, these are the plans you can choose from:

Silver — $0 + state fee

  • Prepare and file articles of organization
  • Unlimited name availability search
  • Free registered agent (for first year)
  • Statement and resignation of organizer
  • Statement and resignation of the incorporator**
  • Online status tracking
  • Online document access
  • Lifetime company alerts
  • Standard state filing
  • Electronic document delivery

Gold — $149 + state fee

  • All Silver services
  • EIN/tax ID
  • Operating agreement
  • IRS form 2553
  • Operating agreement
  • Banking resolution
  • Express shipping
  • Unlimited phone and email support
  • Corporate bylaws**
  • Corporation organizational meeting minutes**

Platinum — $299 + state fee

  • All Gold services
  • Business contract templates
  • Free domain name
  • Free email service
  • Expedited filing

**Services specific to corporations

Overall, the Gold package provides the best value. Things like an EIN and more are required to form an LLC or incorporation. That doesn’t come standard with the Silver plan.

I can see why some people would be enticed by the “free” aspect of the Silver package. But the $149 plus state fees for the Gold plan is such a small price to pay for starting a business.

Most people won’t need the Platinum package. The free domain and email service shouldn’t sway you at all. Those are services you should be getting directly from a web hosting provider, not a business formation service. The only real selling point is expedited filing, if you’re in a serious rush.

User Experience

Using Incfile is seamless. Getting started is as simple as navigating to the website, choosing your entity type (LLC, C-corp, S-corp, or nonprofit), and selecting your state.

Incfile will automatically generate your state fees based on the entity you want to form.

Once you sign up and choose your plan, everything is straightforward from there. You’ll enter details about your business and manage it all from Incfile’s intuitive dashboard.

You can track the status of the formation process online. Once you’re officially registered, you can use the dashboard to access crucial documents. Your registered agent will notify you via SMS or email if something requires your attention (like an urgent piece of legal mail).

Overall, the interface is really clean, and it’s easy for anyone to navigate and figure out.

Customer Support

One other key of choosing the Gold plans from Incfile is that you get unlimited phone and email support. Whether you need help finding a document or just have a general inquiry, the Incfile team will be there to provide assistance.

Phone support is available from Monday to Friday, 9 am – 6 pm CST.

As previously mentioned, Incfile has outstanding registered agent services as well. Due to the human touch associated with this service, I think it’s worth mentioning in the customer support category.

Your registered agent can receive documentation on your behalf, ensure you stay compliant, forward your mail, and alert you if something comes up.

Most of the online reviews from existing customers say great things about Incfile’s support. With that said, there are a handful of people who weren’t exactly satisfied. I saw a few recent reviews from people who said they had trouble reaching an agent. But those seemed to be outliers compared to the bulk of reviews.

Final Verdict

If you need to form an LLC, C-Corp, S-Corp, or nonprofit corporation, Incfile should definitely be taken into consideration.

They provide a cost-efficient alternative to hiring a lawyer. Some of their services are completely free; you just have to pay the state fees.

When you sign up for one of Incfile’s business formation plans, you’ll also get registered agent services free for one year. It’s tough to pass up on a value like that.

Workzone Review

Workzone is a project management tool that strikes the right balance between powerful features and accessibility for beginners. The software has been around since 2002 and has incrementally improved its performance over time, proving itself to be a robust solution that you can count on to get the job done. It performs well against its competitors, specifically in the areas of customer service and ease of use.

Workzone Pros and Cons

Pros

  • Customizable control panels
  • Easy to use
  • Stable service
  • Mobile and desktop
  • Great customer support

Cons

  • Can be expensive
  • Minimal reporting
Compare the Best PM Software
We’ve reviewed a wide array of project management software options and determined the top picks for a variety of needs.
See Top Picks

How Workzone Compares to Top Project Management Software Options

Workzone is up against some top competitors in the space, but it does well to meet them in terms of customer service and ease of use. There are a few areas where its competitors offer something better—for instance, Workzone’s reporting functionality is limited, and the price can quickly add up for multiple users. The software is best suited for those that want a secure platform, friendly and helpful staff, and solid project management tools. In other words, it gets the basics right but doesn’t always match the scope of what’s on offer elsewhere.

Great alternatives to Workzone are Zoho Projects and LiquidPlanner. Zoho Projects is one of the best pieces of project management software available today due to its affordable nature and compelling set of tools. LiquidPlanner, meanwhile, is best for complex projects with many contributors and offers quick insight into tasks, risks, and budgets.

Workzone is still a solid choice, though, so read on to learn more about it and whether it might be the right tool for you.

Workzone Team Size

Your team’s size and the number of projects you’ll be working on can significantly affect how suitable a piece of project management software is for you. For example, a smaller team working on a single project may not need paid software from the start—free software might suffice. With Workzone, teams from around five to 500+ people are the intended target here, although it does try to cater to teams smaller than that, so one to four people.

For instance, “Workzone Team” is the starting option, available for $24/user per month. This level features project templates, unlimited workspaces, and time tracking and resource management functionality. It’s a good choice for teams with at least five people, and while free software may be able to cater to smaller teams, likely, it won’t come with the advanced features available here. The second pricing option is available for teams that require more resources, and the third tier is more for an enterprise-level business that needs it all.

Workzone does try to cater to teams and companies of all sizes then, and it does this well, but of course, there’s always going to be something of a “model” team size for each project management software. Workzone’s ideal for smaller and medium-sized businesses overall. Enterprises do receive a viable option in the third tier, but I think you’d be even better off with LiquidPlanner or Celoxis due to their advanced features.

Workzone Ease of Use

Workzone is an easy-to-use piece of software. The interface is accessible enough for most users, and it’s easy to set up and get started with, more so than some of its competitors. For example, creating a new template is simple and follows almost the same process to create a new project. These processes are easy to follow and are backed up by handy tutorial videos to help users.

That said, it’s not exactly what I would call the leading project management interface on the market. The UI can appear to be a little dated in places. This is attributed to the platform’s slow evolution over time—it’s been around since 2002. Some long-time users of the software feel like Workzone is easy enough to set up, but there’s a larger learning curve involved to get the most out of it. The tutorial videos are rarely considered to be optional for most users.

Of course, the benefit of this is that the software is more feature-rich than some of its competitors, and this is helped further by the excellent customer support on offer, something I’ll talk about later on in this review. As long as you’re aware there’s some learning involved to use the tool to the best of its ability, then it’s still going to be a good choice to consider. Just don’t expect to understand everything on day one.

Workzone Price

I’ve briefly mentioned the price of Workzone, but let’s dive down into it a bit further. We’re looking at a pricing structure that appears to be straightforward, with three core tiers–Team, Professional, and Enterprise. These go up in price accordingly and naturally offer enhanced resources and features the more you’re willing to pay each month. Nice and simple, right? Well, not quite.

There can be some confusion over the exact price here per month. Each tier is based upon a cost per user per month. So companies won’t be looking at these prices pictured alone. For instance, a team of one to four users actually starts at $200 per month. Workzone then goes on to further clarify, in fine print, that for a team level, the minimum price is $40 per user, per month, for five users. I think this adds some unnecessary complication to what could otherwise be a straightforward offer. It also means that you pay for a set price regardless of the number of users in that range.

This is to cover costs, and that’s understandable. It does reward small teams with advanced features they wouldn’t find in free project management software. Nevertheless, this could certainly be more transparent for customers.

What’s more, depending on the number of users your company has, it’s fair to say that things can very quickly add up with Workzone. If you have a large team, there’s a good chance you have more resources and can therefore afford more, but this won’t always be true. So be sure to keep in mind the total price here.

Workzone Security

Companies of all sizes want to know their data is safe and secure from cyber attacks, now more than ever in an increasingly remote working environment. They’ll be pleased to know that Workzone offers 256-bit SSL encryption, or in layman terms, substantial security. A hacker would find it nearly impossible to break into security this high, even on the fastest and most powerful supercomputers. Nothing is ever 100% secure, but you can rest pretty easy here, for now at least.

That aside, there are optional settings for the security within Workzone that add some flexibility to things. For instance, a user can restrict access by folder, document, or project to others and choose which users can access it. Unauthorized users won’t even be able to see this information in the first place. Additionally, Workzone backs up its servers every hour, which is great for added peace of mind.

Workzone also uses a firewall to block outside access to its server, making it difficult for email viruses to get through its network. The servers themselves are monitored for possible network attacks on a 24/7 basis. Long-term users have been more than impressed with the service’s stability overall, noting a lack of downtime and serious problems. When it comes to all-important security for your company then, Workzone is unlikely to disappoint.

Workzone Document Sharing

Workzone features document sharing across its entire platform. Users can share files securely with other team members and have the option to give appropriate information as required. Users can also send email alerts of updated files, approvals, and comments. These email alerts can be sent when there’s something new to look at, anything from comments, events tasks, and more. A link handily takes the user directly to the item within the Workzone platform.

It’s possible to drag and drop files on your desktop right into Workzone—one of those small features that’s nice to see. It’s worth mentioning that managers who interact with clients can set folder or document permissions, too, allowing individuals to only see the correct info at the right time. This can be critical to businesses who need to manage clients but don’t want them to see how things work behind the scenes.

Every document has a specific comment area where users can discuss changes that are needed. They can also record meeting notes and things like the next steps on a project. Impressively, throughout these updates, the entire team stays informed, including a time-stamped record of all of the most relevant discussions. While all of this might seem like basic functionality, it’s something that a piece of project management software needs to offer, and I’m pleased to see Workzone provide it.

Workzone Personalization

When you’re working on a platform for a long time, it’s important that you’re able to customize it how you wish, not just for individual comfort but also organizational matters. In other words, to stay on top of things.

Workzone provides the ability to customize your dashboard with logos and different colors. You’ll also be able to create a private portal or extranet for each client to make sure you can take care of them. Setting up workspaces for each client is available too, and you can change their custom branding, adding to a sense of overall professionalism.

Granted, none of this is groundbreaking stuff, yet it’s the sort of thing I like to see offered to businesses as it helps to justify what can be a steep introductory price. Not only are these personalization features good in terms of teamwork and structure, but they’re also a healthy way of boosting productivity. Most of Workzone’s competitors offer a similar customization level, but that doesn’t mean it’s not a welcome addition to the package.

Workzone Customer Support

Workzone’s customer support is perhaps a high point for the software. I mentioned earlier that it could be hard to get over that initial learning curve, but it’s fair to say that Workzone does its best to help you. Not only is the customer support friendly and welcoming, but it’s also very good at answering questions and helping users that get stuck along the way. This in some ways helps to mitigate the overall trouble of having long-term success on the platform.

It’s important to mention that this support isn’t limited either—it’s produced with a quick turnaround time, and it’s always provided by a human being, not a robot that lacks that personal touch. In fact, users have rated the customer service close to perfect across the board. What helps here is that Workzone knows what it’s talking about—it has been helping teams since 2002 with the lessons it has learned along the way.

The support itself is offered to customers in two primary forms: phone and email.

The customer support also includes dedicated and complete training at different levels. For example, system admins are trained to use advanced features, while basic training is available for other users who need to feel comfortable navigating their way around.

You’ll be meeting your customer support every quarter with a dedicated Customer Success Manager that will help to keep things moving smoothly. It’s here you can also ask further questions or queries, with the Success Manager keeping track of each team, what stage they’ve reached, and thinking about where they might need to provide you with some extra assistance.

It’s this responsive, professional approach coupled with a strong knowledge of project management that lends Workzone this well-earned reputation.

Compare the Best PM Software
We’ve reviewed a wide array of project management software options and determined the top picks for a variety of needs.
See Top Picks

Summary

Workzone might not be the very best project management software, and we’d be tempted to recommend alternatives such as Zoho Projects over it. That said, don’t get the wrong impression. A lot of this does also come down to what exactly you’re looking for, and I think in the areas of customer support, a secure and stable service, and its relative ease of use with expert training to back it up, Workzone would be a respectable choice in the right scenario.

As long as you keep the somewhat confusing price in mind and don’t expect the most up-to-date and eye-catching offering that competitors provide, this is a simple, honest, and reliable piece of software that will allow you to accomplish almost everything you need.

Dynamic Favicons for WordPress

Typically, a single favicon is used across a whole domain. But there are times you wanna step it up with different favicons depending on context. A website might change the favicon to match the content being viewed. Or a site might allow users to personalize their theme colors, and those preferences are reflected in the favicon. Maybe you’ve seen favicons that attempt to alert the user of some event.

Multiple favicons can technically be managed by hand — Chris has shown us how he uses two different favicons for development and production. But when you reach the scale of dozens or hundreds of variations, it’s time to dynamically generate them.

This was the situation I encountered on a recent WordPress project for a directory of colleges and universities. (I previously wrote about querying nearby locations for the same project.) When viewing a school’s profile, we wanted the favicon to use a school’s colors rather than our default blue, to give it that extra touch.

With over 200 schools in the directory and climbing, we needed to go dynamic. Fortunately, we already had custom meta fields storing data on each school’s visual identity. This included school colors, which were being used in the stylesheet. We just needed a way to apply these custom meta colors to a dynamic favicon.

In this article, I’ll walk you through our approach and some things to watch out for. You can see the results in action by viewing different schools.

Each favicon is a different color in the tabs based on the school that is selected.

SVG is key

Thanks to improved browser support for SVG favicons, implementing dynamic favicons is much easier than days past. In contrast to PNG (or the antiquated ICO format), SVG relies on markup to define vector shapes. This makes them lightweight, scaleable, and best of all, receptive to all kinds of fun.

The first step is to create your favicon in SVG format. It doesn’t hurt to also run it through an SVG optimizer to get rid of the cruft afterwards. This is what we used in the school directory:

Hooking into WordPress

Next, we want to add the favicon link markup in the HTML head. How to do this is totally up to you. In the case of WordPress, it could be added it to the header template of a child theme or echo’d through a wp_head() action.

function ca_favicon() {
  if ( is_singular( 'school' ) ) {
    $post_id = get_the_ID();
    $color = get_post_meta( $post_id, 'color', true );

    if ( isset( $color ) ) {
      $color = ltrim( $color, '#' ); // remove the hash
      echo '<link rel="icon" href="' . plugins_url( 'images/favicon.php?color=' . $color, __FILE__ ) . '" type="image/svg+xml" sizes="any">';
    }
  }
}
add_filter( 'wp_head' , 'ca_favicon' );

Here we’re checking that the post type is school, and grabbing the school’s color metadata we’ve previously stored using get_post_meta(). If we do have a color, we pass it into favicon.php through the query string.

From PHP to SVG

In a favicon.php file, we start by setting the content type to SVG. Next, we save the color value that’s been passed in, or use the default color if there isn’t one.

Then we echo the large, multiline chunk of SVG markup using PHP’s heredoc syntax (useful for templating). Variables such as $color are expanded when using this syntax.

Finally, we make a couple modifications to the SVG markup. First, classes are assigned to the color-changing elements. Second, a style element is added just inside the SVG element, declaring the appropriate CSS rules and echo-ing the $color.

Instead of a <style> element, we could alternatively replace the default color with $color wherever it appears if it’s not used in too many places.

<?php
header( 'Content-Type: image/svg+xml' );

$color = $_GET[ 'color' ] ?? '065281';
$color = sanitize_hex_color_no_hash( $color );

echo <<<EOL
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000">
  <style type="text/css">
  <![CDATA[
  .primary {
    fill: #$color;
  }
  .shield {
    stroke: #$color;
  }
  ]]>
  </style>
  <defs>
    <path id="a" d="M0 34L318 0l316 34v417.196a97 97 0 01-14.433 50.909C483.553 722.702 382.697 833 317 833S150.447 722.702 14.433 502.105A97 97 0 010 451.196V34z"/>
  </defs>
  <g fill="none" fill-rule="evenodd">
    <g transform="translate(183 65)">
      <mask id="b" fill="#fff">
        <use xlink:href="#a"/>
      </mask>
      <use fill="#FFF" xlink:href="#a"/>
      <path class="primary" mask="url(#b)" d="M317-37h317v871H317z"/>
      <path class="primary" mask="url(#b)" d="M0 480l317 30 317-30v157l-317-90L0 517z"/>
      <path fill="#FFF" mask="url(#b)" d="M317 510l317-30v37l-317 30z"/>
    </g>
    <g fill-rule="nonzero">
      <path class="primary" d="M358.2 455.2c11.9 0 22.633-.992 32.2-2.975 9.567-1.983 18.375-4.9 26.425-8.75 8.05-3.85 15.458-8.458 22.225-13.825 6.767-5.367 13.3-11.433 19.6-18.2l-34.3-34.65c-9.567 8.867-19.192 15.867-28.875 21-9.683 5.133-21.525 7.7-35.525 7.7-10.5 0-20.125-2.042-28.875-6.125s-16.217-9.625-22.4-16.625-11.025-15.167-14.525-24.5-5.25-19.25-5.25-29.75v-.7c0-10.5 1.75-20.358 5.25-29.575 3.5-9.217 8.4-17.325 14.7-24.325 6.3-7 13.825-12.483 22.575-16.45 8.75-3.967 18.258-5.95 28.525-5.95 12.367 0 23.508 2.45 33.425 7.35 9.917 4.9 19.658 11.667 29.225 20.3l34.3-39.55a144.285 144.285 0 00-18.2-15.4c-6.533-4.667-13.65-8.633-21.35-11.9-7.7-3.267-16.275-5.833-25.725-7.7-9.45-1.867-19.892-2.8-31.325-2.8-18.9 0-36.167 3.325-51.8 9.975-15.633 6.65-29.05 15.75-40.25 27.3s-19.95 24.967-26.25 40.25c-6.3 15.283-9.45 31.675-9.45 49.175v.7c0 17.5 3.15 33.95 9.45 49.35 6.3 15.4 15.05 28.758 26.25 40.075 11.2 11.317 24.5 20.242 39.9 26.775 15.4 6.533 32.083 9.8 50.05 9.8z"/>
      <path fill="#FFF" d="M582.35 451l22.4-54.95h103.6l22.4 54.95h56.35l-105-246.75h-49.7L527.4 451h54.95zM689.1 348.45H624L656.55 269l32.55 79.45z"/>
    </g>
    <path class="shield" stroke-width="30" d="M183 99l318-34 316 34v417.196a97 97 0 01-14.433 50.909C666.553 787.702 565.697 898 500 898S333.447 787.702 197.433 567.105A97 97 0 01183 516.196V99h0z"/>
  </g>
</svg>
EOL;
?>

With that, you’ve got a dynamic favicon working on your site.

Security considerations

Of course, blindly echo-ing URL parameters opens you up to hacks. To mitigate these, we should sanitize all of our inputs.

In this case, we‘re only interested in values that match the 3-digit or 6-digit hex color format. We can include a function like WordPress’s own sanitize_hex_color_no_hash() to ensure only colors are passed in.

function sanitize_hex_color( $color ) {
  if ( '' === $color ) {
    return '';
  }

  // 3 or 6 hex digits, or the empty string.
  if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
    return $color;
  }
}

function sanitize_hex_color_no_hash( $color ) {
  $color = ltrim( $color, '#' );

  if ( '' === $color ) {
    return '';
  }

  return sanitize_hex_color( '#' . $color ) ? $color : null;
}

You’ll want to add your own checks based on the values you want passed in.

Caching for better performance

Browsers cache SVGs, but this benefit is lost for PHP files by default. This means each time the favicon is loaded, your server’s being hit.

To reduce server strain and improve performance, it’s essential that you explicitly cache this file. You can configure your server, set up a page rule through your CDN, or add a cache control header to the very top of favicon.php like so:

header( 'Cache-Control: public, max-age=604800' );  // 604,800 seconds or 1 week

In our tests, with no caching, our 1.5 KB SVG file took about 300 milliseconds to process on the first load, and about 100 milliseconds on subsequent loads. That’s pretty lousy. But with caching, we brought this down to 25 ms from CDN on first load, and 1 ms from browser cache on later loads — as good as a plain old SVG.

Browser support

If we were done there, this wouldn’t be web development. We still have to talk browser support.

As mentioned before, modern browser support for SVG favicons is solid, and fully-supported in current versions of Chrome, Firefox, and Edge.

SVG favicons have arrived… except in Safari.

One caveat is that Firefox requires the attribute type="image/svg+xml" in the favicon declaration for it to work. The other browsers are more forgiving, but it‘s just good practice. You should include sizes="any" while you’re at it.

<link rel="icon" href="path/to/favicon.svg" type="image/svg+xml" sizes="any">

Safari

Safari doesn‘t support SVG favicons as of yet, outside of the mask icon feature intended for pinned tabs. In my experimentation, this was buggy and inconsistent. It didn’t handle complex shapes or colors well, and cached the same icon across the whole domain. Ultimately we decided not to bother and just use a fallback with the default blue fill until Safari improves support.

Fallbacks

As solid as SVG favicon support is, it‘s still not 100%. So be sure to add fallbacks. We can set an additional favicon for when SVG icons aren’t supported with the rel="alternative icon" attribute:

<link rel="icon" href="path/to/favicon.svg" type="image/svg+xml" sizes="any">
<link rel="alternate icon" href="path/to/favicon.png" type="image/png">

To make the site even more bulletproof, you can also drop the eternal favicon.ico in your root.

Going further

We took a relatively simple favicon and swapped one color for another. But taking this dynamic approach opens the door to so much more: modifying multiple colors, other properties like position, entirely different shapes, and animations.

For instance, here’s a demo I’ve dubbed Favicoin. It plots cryptocurrency prices in the favicon as a sparkline.

Implementing dynamic favicons like this isn’t limited to WordPress or PHP; whatever your stack, the same principles apply. Heck, you could even achieve this client-side with data URLs and JavaScript… not that I recommend it for production.

But one thing‘s for sure: we’re bound to see creative applications of SVG favicons in the future. Have you seen or created your own dynamic favicons? Do you have any tips or tricks to share?


The post Dynamic Favicons for WordPress appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

miss rose makeup

please told me the technique that create higher rank of mine website through key words just miss rose makeup is cosmetic website

7+ Best WordPress Accordion Plugins (2024)

Are you looking for the best WordPress accordion plugins?

An accordion is a helpful web design element that allows you to display content in a collapsible tab layout. It’s often used to display frequently asked questions or product details without taking up so much of the page’s space.

In this article, we will share the best WordPress accordion plugins that you can use on your website.

Best WordPress Accordion Plugins

What Is an Accordion on a Website?

An accordion is a list of panels that can hide and show information when you click on each panel. It lets users choose which detail they want to learn more about, while the rest of the tabbed content remains closed.

An example of a FAQ section using an accordion

Many website owners use accordions when they want to cover tons of information while saving some space on their web pages.

You may have seen accordions used as FAQs (Frequently Asked Questions) in online documentation. Users can click on the question to see the answer, and if they don’t want to read it, then they can just close it again. FAQ content has also become more common for WordPress SEO since it can show up in the search results.

WooCommerce product pages also often use accordions to organize a lot of information. Customers can click on a detail they want more explanation for.

By default, WordPress doesn’t have a Gutenberg block for accordions. That’s why many users install a plugin for this purpose.

Let’s take a look at some of the best WordPress accordion plugins that you can use on your WordPress website.

Disclaimer: When choosing the plugins for this showcase, we installed each tool on our test site to explore its pros, cons, and features. This allowed us to recommend only the best WordPress accordion plugins.

1. Heroic FAQs

If you want to use an accordion plugin specifically for frequently asked questions, then check out Heroic FAQs. It lets you easily generate collapsible tabbed content and add it anywhere on your WordPress blog or site.

It comes with a drag-and-drop editor so that you can create questions and answers, group FAQs, and reorder items with ease. There are 5 pre-defined accordion styles and many customization options to match your website design perfectly.

For example, you can add rich content to your accordions, including images, blockquotes, videos, lists, and so on.

Heroic FAQs also offers 15 FAQ icon choices, accordion or toggle style options, CSS3 animations, and more to make your FAQs more personalized.

Pros of Heroic FAQs:

  • Easy-to-customize accordions for FAQ content.
  • User-friendly drag-and-drop editor to build your accordions.
  • Offers rich content elements to be inserted into your accordions so you can make them much more engaging.
  • Has built-in FAQ schema support so that your FAQ content can show up on search engines.

Cons of Heroic FAQs:

  • No free plugin is available.
  • Must be purchased with Heroic KB, where the price starts at $149.50 per year. So, this plugin is kind of like an addon.

Why we chose Heroic FAQs: It is perfect if you are looking for an easy-to-use accordion plugin that is specifically designed for FAQ content. We also recommend it if you want to build a knowledge base, as you have to purchase it with Heroic KB.

2. SeedProd

SeedProd Website and Theme Builder

As the best drag-and-drop WordPress page builder in the market, SeedProd offers various blocks to create a user-friendly site. These include an accordion block, which lets you add text sections that expand and collapse on your pages.

All you have to do is drag and drop the accordion block onto your page and start customizing it. You can create as many text sections as you need and even use a unique dropdown icon from Font Awesome for a personal touch.

Creating an accordion in SeedProd

Feel free to customize the block’s typography, colors, animated effects, and more to make the accordion more interactive.

What’s great about SeedProd is that it makes it easy to make a responsive accordion. You can adjust the block’s spacing for desktop and mobile devices, and even opt to hide the accordion if the page is viewed on a certain device.

Making the SeedProd accordion mobile-friendly

Pros of SeedProd:

  • Easy-to-use drag-and-drop editor for users of all levels.
  • The customization options allow full control over the accordion’s design, from the header and dropdown icon to the text section’s shadows.
  • Provides settings to adjust the accordion’s spacing and visibility on mobile, which is great for improving the user experience.
  • Offers various ready-to-use FAQ section layouts to speed up your process.
  • Allows custom CSS to make your accordion design more personalized.

Cons of SeedProd:

Why we chose SeedProd: While the accordion block is only available in the Pro version, we still think SeedProd is worth recommending. Besides a customizable accordion, you will also get access to a user-friendly page builder that makes creating a unique website easy.

For more details, you can see our complete SeedProd review.

3. Accordions

Accordions is a slightly more advanced accordion plugin, but it’s a pretty powerful one, too.

Unlike the previous plugins, it doesn’t come with a WYSIWYG or drag-and-drop editor. The interface is much simpler, with no live preview, but you can see a lot of settings to add and customize unlimited accordions.

The Accordions plugin interface in WordPress

One of the most notable features of this plugin is its lazy load setting. This delays the loading of the accordion until the user’s screen reaches it on the page. This way, you can maintain a fast website performance.

Additionally, if you get the Pro version, you can create nested accordions. This feature will be handy if you have plenty of information to explain and want to make it more organized.

Pros of Accordions:

  • Has a lazy loading feature to optimize the accordions.
  • Offers an import feature for importing accordions from other plugins.
  • Provides two types of accordions to choose from: the traditional accordion or vertical tabs.
  • Offers integration with Font Awesome icons to customize the icons for expanding or collapsing the accordion.
  • The Pro plan includes advanced features like nested accordions and a built-in search function.

Cons of Accordions:

  • The interface is not the most user-friendly.
  • You can only display the accordion using shortcodes (there’s no built-in block for it).

Why we chose Accordions: Despite its slight lack of user-friendliness, Accordions is a powerful plugin for creating beautiful accordions. Its Pro plan also includes features that you may not find in other plugins, like nested accordions.

4. Thrive Architect

Thrive Architect

Thrive Architect is another great page builder with an accordion/toggle block. It has a drag-and-drop editor, so designing a FAQ or product information section with this plugin is easy.

What makes Thrive Architect unique is you can customize the toggle block’s animation style and speed. This offers you more control over how the accordion behaves.

Editing the toggle block in Thrive Architect

Other than that, you can create columns in the toggle block, which can be beneficial if you have a lot of accordion content. Plus, you can customize the toggle block’s background color, typography, shadows, and so on.

If you want to make the toggle expanded by default to catch visitors’ attention, you can do that too. You don’t have to worry about the space becoming limited due to the expansion, as only the first accordion’s content will be shown.

Pros of Thrive Architect:

  • The toggle block comes with all Thrive Architect and Thrive Suite plans.
  • User-friendly drag-and-drop editor.
  • Plenty of ways to customize how the accordion block looks.
  • Advanced settings to configure the accordion block’s animation to control how it behaves when clicked.
  • Settings to make the toggle visible or invisible on desktop, mobile, and tablet to make the website more responsive.

Cons of Thrive Architect:

  • No free version is available.
  • No auto-save feature, so you have to manually save your work.

Why we chose Thrive Architect: If you are on the hunt for a page builder with a toggle function, then try Thrive Architect. The customization settings to create accordions are comprehensive, and you can easily build a unique web design and attractive landing pages with the plugin.

See our Thrive Architect review for more information.

5. Ultimate Blocks

Ultimate Blocks

Ultimate Blocks is a Gutenberg block plugin with an accordion feature. Using it, you can add a content toggle block right in the block editor rather than in a separate interface.

This way, you can create a FAQ section and easily place it wherever you want on your page or post.

The cool thing about Ultimate Blocks is you can select the heading tag for the accordion title. As a result, you can make your accordion content SEO-friendly and align it with the logical structure of your page or post.

If you upgrade to the Pro version, then you can also add a search bar on top of the accordion so that users can search for answers quickly.

Pros of Ultimate Blocks:

  • Offers responsive design controls to make the accordion hidden on desktop, tablet, or mobile devices.
  • SEO-friendly, with FAQ schema support and customizable heading tags.
  • Easy to use as you can add the content toggle block right in the drag-and-drop Gutenberg block editor.
  • Provides other blocks for presenting information while minimizing space, like tabbed and expandable content blocks.

Cons of Ultimate Blocks:

  • Limited accordion icon options compared to the other plugins on the list.

Why we chose Ultimate Blocks: Most WordPress users are already pretty familiar with the block editor, so Ultimate Blocks is great for adding an accordion right in the editor. The content toggle block is also available in the free plugin version.

To learn more, see our detailed Ultimate Blocks review.

6. Easy Accordion

Easy Accordion

If you are looking for an accordion plugin for your WooCommerce product pages, then check out Easy Accordion. The Pro version includes support for creating detailed FAQ sections for product pages.

If you have the same FAQ for multiple product categories, you can easily implement them with this plugin. As a result, you won’t have to create different FAQs for different pages.

Easy Accordion is also translation-ready, as it is compatible with multilingual plugins like WPML and PolyLang. This is great if you run a multilingual business.

Another great feature is the custom shortcode generator. This lets you easily differentiate your accordion shortcodes for when you need to embed them later.

Pros of Easy Accordion Pro:

  • Supports Gutenberg and most major WordPress page builder plugins.
  • You can create accordions from existing pages, posts, custom post types, and even taxonomies to save time on inserting the accordion content.
  • Multiple accordion layouts, from vertical tabs and multicolumn sections to horizontal toggles.
  • Includes SEO features like schema markup and nofollow links.

Cons of Easy Accordion Pro:

  • The plugin comes with a free version, but most of the features that make it great are locked in the Pro version.

Why we chose Easy Accordion: The built-in support for WooCommerce product pages and translation plugins makes Easy Accordion great for online businesses.

7. Accordion FAQ

WPShopSmart's Accordion plugin

If you are looking for a no-frills accordion plugin with a drag-and-drop builder, then look no further than Accordion FAQ. With it, you can easily create multiple collapsible content sections and move them however you likef from the backend.

The free Accordion FAQ plugin only has one template, but it’s quite user-friendly and versatile. For more design choices, you can upgrade to the Pro version and get 18+ templates.

WPShopSmart's Accordion plugin interface in the admin area

The plugin also offers many ways to customize the accordion. Feel free to use its 30+ animation styles, dozens of Font Awesome Icons, and 500+ Google Fonts to make the accordion more unique.

Pros of Accordion FAQ:

  • User-friendly drag-and-drop accordion editor.
  • Many customization options with unlimited color options and tons of animation styles, icons, and Google Fonts.
  • The pricing for the Pro plans is quite affordable, starting from $9 for 6 months.
  • Based on the Bootstrap framework, which creates a responsive design for the accordions.

Cons of Accordion FAQ:

  • The free plugin version displays multiple ads that can make for a poor user experience.

Why we chose Accordion FAQ: If you are looking for a simple accordion plugin with a drag-and-drop functionality, then Accordion FAQ is a great option, so long as you don’t mind the ads on the plugin page.

Bonus: Shortcodes Ultimate

The Shortcodes Ultimate plugin

Shortcodes Ultimate is a WordPress plugin that comes with a set of shortcodes to add various content elements, including accordions.

Since it uses shortcodes, you can add the accordion shortcode to any part of your WordPress site, from pages and posts to widget-ready areas. You also won’t have to worry about theme compatibility, as it functions smoothly with most modern themes.

The downside is the shortcode and HTML for the accordion are pretty long, and you have to be careful when inserting your content. That’s why we didn’t put this option in the same list as above.

For more details, see our complete Shortcodes Ultimate review.

What Is the Best WordPress Accordion Plugin?

The best WordPress accordion plugin depends on your needs.

If you are looking for an accordion plugin for FAQ content, then the best option is Heroic FAQs. The drag-and-drop editor is easy to use and includes various customization features for creating accordions out of the box.

If you are looking for a page builder with an accordion block, then SeedProd is your best option. SeedProd offers various controls and settings for how the accordion looks and behaves. Plus, your accordion design will look great with other elements on your page.

Finally, for people looking for a free WordPress accordion plugin, consider Accordions. This plugin includes everything needed to create simple accordions or vertical tabbed content. Its lazy loading will also ensure the accordion won’t slow down your site.

FAQs About Accordions in WordPress

Let’s go over some frequently asked questions about adding accordions in WordPress.

How do I add an accordion to WordPress?

The easiest way to add an accordion in WordPress is with a plugin like Heroic FAQs, SeedProd, or Accordions. With these tools, you can insert a ready-to-use accordion into your pages and posts and customize it to your liking.

Which WordPress accordion plugin offers the best customization options?

Heroic FAQs offers the best customization options for creating accordions. It comes with a drag-and-drop editor, pre-defined accordion styles, 15 FAQ icon choices, CSS3 animations, toggle style options, and more.

SeedProd also offers comprehensive customization options to modify how an accordion looks and behaves. Besides changing the color and typography, you can adjust the accordion’s size specifically for mobile devices.

Which WordPress accordion plugin is best for creating FAQ sections?

Heroic FAQs is an excellent WordPress accordion plugin for creating FAQ sections. With this plugin, you can create accordions using various styles and insert different rich content to make your information easier to understand.

Best Guides for Using Accordions in WordPress

We hope this article helped you find the best WordPress accordion plugins. You may also want to see our picks of the best email marketing services, and our expert list of the must-have WordPress plugins for business websites.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post 7+ Best WordPress Accordion Plugins (2024) first appeared on WPBeginner.

Adding A Commenting System To A WYSIWYG Editor

In recent years, we’ve seen Collaboration penetrate a lot of digital workflows and use-cases across many professions. Just within the Design and Software Engineering community, we see designers collaborate on design artifacts using tools like Figma, teams doing Sprint and Project Planning using tools like Mural and interviews being conducted using CoderPad. All these tools are constantly aiming to bridge the gap between an online and a physical world experience of executing these workflows and making the collaboration experience as rich and seamless as possible.

For the majority of the Collaboration Tools like these, the ability to share opinions with one another and have discussions about the same content is a must-have. A Commenting System that enables collaborators to annotate parts of a document and have conversations about them is at the heart of this concept. Along with building one for text in a WYSIWYG Editor, the article tries to engage the readers into how we try to weigh the pros and cons and attempt to find a balance between application complexity and user experience when it comes to building features for WYSIWYG Editors or Word Processors in general.

Representing Comments In Document Structure

In order to find a way to represent comments in a rich text document’s data structure, let’s look at a few scenarios under which comments could be created inside an editor.

  • Comments created over text that has no styles on it (basic scenario);
  • Comments created over text that may be bold/italic/underlined, and so on;
  • Comments that overlap each other in some way (partial overlap where two comments share only a few words or fully-contained where one comment’s text is fully contained within text of another comment);
  • Comments created over text inside a link (special because links are nodes themselves in our document structure);
  • Comments that span multiple paragraphs (special because paragraphs are nodes in our document structure and comments are applied to text nodes which are paragraph’s children).

Looking at the above use-cases, it seems like comments in the way they can come up in a rich text document are very similar to character styles (bold, italics etc). They can overlap with each other, go over text in other types of nodes like links and even span multiple parent nodes like paragraphs.

For this reason, we use the same method to represent comments as we do for character styles, i.e. “Marks” (as they are so called in SlateJS terminology). Marks are just regular properties on nodes — speciality being that Slate’s API around marks (Editor.addMark and Editor.removeMark) handles changing of the node hierarchy as multiple marks get applied to the same range of text. This is extremely useful to us as we deal with a lot of different combinations of overlapping comments.

Comment Threads As Marks

Whenever a user selects a range of text and tries to insert a comment, technically, they’re starting a new comment thread for that text range. Because we would allow them to insert a comment and later replies to that comment, we treat this event as a new comment thread insertion in the document.

The way we represent comment threads as marks is that each comment thread is represented by a mark named as commentThread_threadID where threadID is a unique ID we assign to each comment thread. So, if the same range of text has two comment threads over it, it would have two properties set to the truecommentThread_thread1 and commentThread_thread2. This is where comment threads are very similar to character styles since if the same text was bold and italic, it would have both the properties set to truebold and italic.

Before we dive into actually setting this structure up, it’s worth looking at how the text nodes change as comment threads get applied to them. The way this works (as it does with any mark) is that when a mark property is being set on the selected text, Slate’s Editor.addMark API would split the text node(s) if needed such that in the resulting structure, text nodes are set up in a way that each text node has the exact same value of the mark.

To understand this better, take a look at the following three examples that show the before-and-after state of the text nodes once a comment thread is inserted on the selected text:

Highlighting Commented Text

Now that we know how we are going to represent comments in the document structure, let’s go ahead and add a few to the example document from the first article and configure the editor to actually show them as highlighted. Since we will have a lot of utility functions to deal with comments in this article, we create a EditorCommentUtils module that will house all these utils. To start with, we create a function that creates a mark for a given comment thread ID. We then use that to insert a few comment threads in our ExampleDocument.

# src/utils/EditorCommentUtils.js

const COMMENT_THREAD_PREFIX = "commentThread_";

export function getMarkForCommentThreadID(threadID) {
  return `${COMMENT_THREAD_PREFIX}${threadID}`;
}

Below image underlines in red the ranges of text that we have as example comment threads added in the next code snippet. Note that the text ‘Richard McClintock’ has two comment threads that overlap each other. Specifically, this is a case of one comment thread being fully contained inside another.

# src/utils/ExampleDocument.js
import { getMarkForCommentThreadID } from "../utils/EditorCommentUtils";
import { v4 as uuid } from "uuid";

const exampleOverlappingCommentThreadID = uuid();

const ExampleDocument = [
   ...
   {
        text: "Lorem ipsum",
        [getMarkForCommentThreadID(uuid())]: true,
   },
   ...
   {
        text: "Richard McClintock",
        // note the two comment threads here.
        [getMarkForCommentThreadID(uuid())]: true,
        [getMarkForCommentThreadID(exampleOverlappingCommentThreadID)]: true,
   },
   {
        text: ", a Latin scholar",
        [getMarkForCommentThreadID(exampleOverlappingCommentThreadID)]: true,
   },
   ...
];

We focus on the UI side of things of a Commenting System in this article so we assign them IDs in the example document directly using the npm package uuid. Very likely that in a production version of an editor, these IDs are created by a backend service.

We now focus on tweaking the editor to show these text nodes as highlighted. In order to do that, when rendering text nodes, we need a way to tell if it has comment threads on it. We add a util getCommentThreadsOnTextNode for that. We build on the StyledText component that we created in the first article to handle the case where it may be trying to render a text node with comments on. Since we have some more functionality coming that would be added to commented text nodes later, we create a component CommentedText that renders the commented text. StyledText will check if the text node it’s trying to render has any comments on it. If it does, it renders CommentedText. It uses a util getCommentThreadsOnTextNode to deduce that.

# src/utils/EditorCommentUtils.js

export function getCommentThreadsOnTextNode(textNode) {
  return new Set(
     // Because marks are just properties on nodes,
    // we can simply use Object.keys() here.
    Object.keys(textNode)
      .filter(isCommentThreadIDMark)
      .map(getCommentThreadIDFromMark)
  );
}

export function getCommentThreadIDFromMark(mark) {
  if (!isCommentThreadIDMark(mark)) {
    throw new Error("Expected mark to be of a comment thread");
  }
  return mark.replace(COMMENT_THREAD_PREFIX, "");
}

function isCommentThreadIDMark(mayBeCommentThread) {
  return mayBeCommentThread.indexOf(COMMENT_THREAD_PREFIX) === 0;
}

The first article built a component StyledText that renders text nodes (handling character styles and so on). We extend that component to use the above util and render a CommentedText component if the node has comments on it.

# src/components/StyledText.js

import { getCommentThreadsOnTextNode } from "../utils/EditorCommentUtils";

export default function StyledText({ attributes, children, leaf }) {
  ...

  const commentThreads = getCommentThreadsOnTextNode(leaf);

  if (commentThreads.size > 0) {
    return (
      <CommentedText
      {...attributes}
     // We use commentThreads and textNode props later in the article.
      commentThreads={commentThreads}
      textNode={leaf}
      >
        {children}
      </CommentedText>
    );
  }

  return <span {...attributes}>{children}</span>;
}

Below is the implementation of CommentedText that renders the text node and attaches the CSS that shows it as highlighted.

# src/components/CommentedText.js

import "./CommentedText.css";

import classNames from "classnames";

export default function CommentedText(props) {
  const { commentThreads, ...otherProps } = props;
  return (
    <span
      {...otherProps}
      className={classNames({
        comment: true,
      })}
    >
      {props.children}
    </span>
  );
}

# src/components/CommentedText.css

.comment {
  background-color: #feeab5;
}

With all of the above code coming together, we now see text nodes with comment threads highlighted in the editor.

Note: The users currently cannot tell if certain text has overlapping comments on it. The entire highlighted text range looks like a single comment thread. We address that later in the article where we introduce the concept of active comment thread which lets users select a specific comment thread and be able to see its range in the editor.

UI Storage For Comments

Before we add the functionality that enables a user to insert new comments, we first setup a UI state to hold our comment threads. In this article, we use RecoilJS as our state management library to store comment threads, comments contained inside the threads and other metadata like creation time, status, comment author etc. Let’s add Recoil to our application:

> yarn add recoil

We use Recoil atoms to store these two data structures. If you’re not familiar with Recoil, atoms are what hold the application state. For different pieces of application state, you’d usually want to set up different atoms. Atom Family is a collection of atoms — it can be thought to be a Map from a unique key identifying the atom to the atoms themselves. It’s worth going through core concepts of Recoil at this point and familiarizing ourselves with them.

For our use case, we store comment threads as an Atom family and then wrap our application in a RecoilRoot component. RecoilRoot is applied to provide the context in which the atom values are going to be used. We create a separate module CommentState that holds our Recoil atom definitions as we add more atom definitions later in the article.

# src/utils/CommentState.js

import { atom, atomFamily } from "recoil";

export const commentThreadsState = atomFamily({
  key: "commentThreads",
  default: [],
});

export const commentThreadIDsState = atom({
  key: "commentThreadIDs",
  default: new Set([]),
});

Worth calling out few things about these atom definitions:

  • Each atom/atom family is uniquely identified by a key and can be set up with a default value.
  • As we build further in this article, we are going to need a way to iterate over all the comment threads which would basically mean needing a way to iterate over commentThreadsState atom family. At the time of writing this article, the way to do that with Recoil is to set up another atom that holds all the IDs of the atom family. We do that with commentThreadIDsState above. Both these atoms would have to be kept in sync whenever we add/delete comment threads.

We add a RecoilRoot wrapper in our root App component so we can use these atoms later. Recoil’s documentation also provides a helpful Debugger component that we take as it is and drop into our editor. This component will leave console.debug logs to our Dev console as Recoil atoms are updated in real-time.

# src/components/App.js

import { RecoilRoot } from "recoil";

export default function App() {
  ...

  return (
    <RecoilRoot>
      >
         ...
        <Editor document={document} onChange={updateDocument} />

    </RecoilRoot>
  );
}
# src/components/Editor.js

export default function Editor({ ... }): JSX.Element {
  .....

  return (
    <>
      <Slate>
         .....
      </Slate>
      <DebugObserver />
   </>
);

function DebugObserver(): React.Node {
   // see API link above for implementation.
}

We also need to need to add code that initializes our atoms with the comment threads that already exist on the document (the ones we added to our example document in the previous section, for instance). We do that at a later point when we build the Comments Sidebar that needs to read all the comment threads in a document.

At this point, we load our application, make sure there are no errors pointing to our Recoil setup and move forward.

Adding New Comments

In this section, we add a button to the toolbar that lets the user add comments (viz. create a new comment thread) for the selected text range. When the user selects a text range and clicks on this button, we need to do the below:

  1. Assign a unique ID to the new comment thread being inserted.
  2. Add a new mark to Slate document structure with the ID so the user sees that text highlighted.
  3. Add the new comment thread to Recoil atoms we created in the previous section.

Let’s add a util function to EditorCommentUtils that does #1 and #2.

# src/utils/EditorCommentUtils.js

import { Editor } from "slate";
import { v4 as uuidv4 } from "uuid";

export function insertCommentThread(editor, addCommentThreadToState) {
    const threadID = uuidv4();
    const newCommentThread = {
        // comments as added would be appended to the thread here.
        comments: [],
        creationTime: new Date(),
        // Newly created comment threads are OPEN. We deal with statuses
        // later in the article.
        status: "open",
    };
    addCommentThreadToState(threadID, newCommentThread);
    Editor.addMark(editor, getMarkForCommentThreadID(threadID), true);
    return threadID;
}

By using the concept of marks to store each comment thread as its own mark, we’re able to simply use the Editor.addMark API to add a new comment thread on the text range selected. This call alone handles all the different cases of adding comments — some of which we described in the earlier section — partially overlapping comments, comments inside/overlapping links, comments over bold/italic text, comments spanning paragraphs and so on. This API call adjusts the node hierarchy to create as many new text nodes as needed to handle these cases.

addCommentThreadToState is a callback function that handles step #3 — adding the new comment thread to Recoil atom . We implement that next as a custom callback hook so that it’s re-usable. This callback needs to add the new comment thread to both the atoms — commentThreadsState and commentThreadIDsState. To be able to do this, we use the useRecoilCallback hook. This hook can be used to construct a callback which gets a few things that can be used to read/set atom data. The one we’re interested in right now is the set function which can be used to update an atom value as set(atom, newValueOrUpdaterFunction).

# src/hooks/useAddCommentThreadToState.js

import {
  commentThreadIDsState,
  commentThreadsState,
} from "../utils/CommentState";

import { useRecoilCallback } from "recoil";

export default function useAddCommentThreadToState() {
  return useRecoilCallback(
    ({ set }) => (id, threadData) => {
      set(commentThreadIDsState, (ids) => new Set([...Array.from(ids), id]));
      set(commentThreadsState(id), threadData);
    },
    []
  );
}

The first call to set adds the new ID to the existing set of comment thread IDs and returns the new Set(which becomes the new value of the atom).

In the second call, we get the atom for the ID from the atom family — commentThreadsState as commentThreadsState(id) and then set the threadData to be its value. atomFamilyName(atomID) is how Recoil lets us access an atom from its atom family using the unique key. Loosely speaking, we could say that if commentThreadsState was a javascript Map, this call is basically — commentThreadsState.set(id, threadData).

Now that we have all this code setup to handle insertion of a new comment thread to the document and Recoil atoms, lets add a button to our toolbar and wire it up with the call to these functions.

# src/components/Toolbar.js

import { insertCommentThread } from "../utils/EditorCommentUtils";
import useAddCommentThreadToState from "../hooks/useAddCommentThreadToState";

export default function Toolbar({ selection, previousSelection }) {
  const editor = useEditor();
  ...

  const addCommentThread = useAddCommentThreadToState();

  const onInsertComment = useCallback(() => {
    const newCommentThreadID = insertCommentThread(editor, addCommentThread);
  }, [editor, addCommentThread]);

return (
    <div className="toolbar">
       ...
      <ToolBarButton
        isActive={false}
        label={<i className={bi ${getIconForButton("comment")}} />}
        onMouseDown={onInsertComment}
      />
    </div>
  );
}

Note: We use onMouseDown and not onClick which would have made the editor lose focus and selection to become null. We’ve discussed that in a little more detail in the link insertion section of the first article.

In the below example, we see the insertion in action for a simple comment thread and an overlapping comment thread with links. Notice how we get updates from Recoil Debugger confirming our state is getting updated correctly. We also verify that new text nodes are created as threads are being added to the document.

In the above example, the user inserts the following comment threads in that order:

  1. Comment Thread #1 over character ‘B’ (length = 1).
  2. Comment Thread #2 over ‘AB’ (length = 2).
  3. Comment Thread #3 over ‘BC’ (length = 2).

At the end of these insertions, because of the way Slate splits the text nodes with marks, we will have three text nodes — one for each character. Now, if the user clicks on ‘B’, going by the shortest length rule, we select thread #1 as it is the shortest of the three in length. If we don’t do that, we wouldn’t have a way to select Comment Thread #1 ever since it is only one-character in length and also a part of two other threads.

Although this rule makes it easy to surface shorter-length comment threads, we could run into situations where longer comment threads become inaccessible since all the characters contained in them are part of some other shorter comment thread. Let’s look at an example for that.

Let’s assume we have 100 characters (say, character ‘A’ typed 100 times that is) and the user inserts comment threads in the following order:

  1. Comment Thread # 1 of range 20,80
  2. Comment Thread # 2 of range 0,50
  3. Comment Thread # 3 of range 51,100

As you can see in the above example, if we follow the rule we just described here, clicking on any character between #20 and #80, would always select threads #2 or #3 since they are shorter than #1 and hence #1 would not be selectable. Another scenario where this rule can leave us undecided as to which comment thread to select is when there are more than one comment threads of the same shortest length on a text node.

For such combination of overlapping comments and many other such combinations that one could think of where following this rule makes a certain comment thread inaccessible by clicking on text, we build a Comments Sidebar later in this article which gives user a view of all the comment threads present in the document so they can click on those threads in the sidebar and activate them in the editor to see the range of the comment. We still would want to have this rule and implement it as it should cover a lot of overlap scenarios except for the less-likely examples we cited above. We put in all this effort around this rule primarily because seeing highlighted text in the editor and clicking on it to comment is a more intuitive way of accessing a comment on text than merely using a list of comments in the sidebar.

Insertion Rule

The rule is:

“If the text user has selected and is trying to comment on is already fully covered by comment thread(s), don’t allow that insertion.”

This is so because if we did allow this insertion, each character in that range would end up having at least two comment threads (one existing and another the new one we just allowed) making it difficult for us to determine which one to select when the user clicks on that character later.

Looking at this rule, one might wonder why we need it in the first place if we already have the Shortest Comment Range Rule that allows us to select the smallest text range. Why not allow all combinations of overlaps if we can use the first rule to deduce the right comment thread to show? As some of the examples we’ve discussed earlier, the first rule works for a lot of scenarios but not all of them. With the Insertion Rule, we try to minimize the number of scenarios where the first rule cannot help us and we have to fallback on the Sidebar as the only way for the user to access that comment thread. Insertion Rule also prevents exact-overlaps of comment threads. This rule is commonly implemented by a lot of popular editors.

Below is an example where if this rule didn’t exist, we would allow the Comment Thread #3 and then as a result of the first rule, #3 would not be accessible since it would become the longest in length.

In this example, let’s assume we don’t wait for intersection to become 0 and just stop when we reach the edge of a comment thread. Now, if the user clicked on #2 and we start traversal in reverse direction, we’d stop at the start of text node #2 itself since that’s the start of the comment thread A. As a result, we might not compute the comment thread lengths correctly for A & B. With the implementation above traversing the farthest edges (text nodes 1,2, and 3), we should get B as the shortest comment thread as expected.

To see the implementation visually, below is a walkthrough with a slideshow of the iterations. We have two comment threads A and B that overlap each other over text node #3 and the user clicks on the overlapping text node #3.

Now that we have all the code in to make selection of comment threads work, let’s see it in action. To test our traversal code well, we test some straightforward cases of overlap and some edge cases like:

  • Clicking on a commented text node at the start/end of the editor.
  • Clicking on a commented text node with comment threads spanning multiple paragraphs.
  • Clicking on a commented text node right before an image node.
  • Clicking on a commented text node overlapping links.

Now that our state is correctly initialized, we can start implementing the sidebar. All our comment threads in the UI are stored in the Recoil atom family — commentThreadsState. As highlighted earlier, the way we iterate through all the items in a Recoil atom family is by tracking the atom keys/ids in another atom. We’ve been doing that with commentThreadIDsState. Let’s add the CommentSidebar component that iterates through the set of ids in this atom and renders a CommentThread component for each.

# src/components/CommentsSidebar.js

import "./CommentSidebar.css";

import {commentThreadIDsState,} from "../utils/CommentState";
import { useRecoilValue } from "recoil";

export default function CommentsSidebar(params) {
  const allCommentThreadIDs = useRecoilValue(commentThreadIDsState);

  return (
    <Card className={"comments-sidebar"}>
      <Card.Header>Comments</Card.Header>
      <Card.Body>
        {Array.from(allCommentThreadIDs).map((id) => (
          <Row key={id}>
            <Col>
              <CommentThread id={id} />
            </Col>
          </Row>
        ))}
      </Card.Body>
    </Card>
  );
}

Now, we implement the CommentThread component that listens to the Recoil atom in the family corresponding to the comment thread it is rendering. This way, as the user adds more comments on the thread in the editor or changes any other metadata, we can update the sidebar to reflect that.

As the sidebar could grow to be really big for a document with a lot of comments, we hide all comments but the first one when we render the sidebar. The user can use the ‘Show/Hide Replies’ button to show/hide the entire thread of comments.

# src/components/CommentSidebar.js

function CommentThread({ id }) {
  const { comments } = useRecoilValue(commentThreadsState(id));

  const [shouldShowReplies, setShouldShowReplies] = useState(false);
  const onBtnClick = useCallback(() => {
    setShouldShowReplies(!shouldShowReplies);
  }, [shouldShowReplies, setShouldShowReplies]);

  if (comments.length === 0) {
    return null;
  }

  const [firstComment, ...otherComments] = comments;
  return (
    <Card
      body={true}
      className={classNames({
        "comment-thread-container": true,
      })}
    >
      <CommentRow comment={firstComment} showConnector={false} />
      {shouldShowReplies
        ? otherComments.map((comment, index) => (
            <CommentRow key={comment-${index}} comment={comment} showConnector={true} />
          ))
        : null}
      {comments.length > 1 ? (
        <Button
          className={"show-replies-btn"}
          size="sm"
          variant="outline-primary"
          onClick={onBtnClick}
        >
          {shouldShowReplies ? "Hide Replies" : "Show Replies"}
        </Button>
      ) : null}
    </Card>
  );
}

We’ve reused the CommentRow component from the popover although we added a design treatment using showConnector prop that basically makes all the comments look connected with a thread in the sidebar.

Now, we render the CommentSidebar in the Editor and verify that it shows all the threads we have in the document and correctly updates as we add new threads or new comments to existing threads.

# src/components/Editor.js

return (
    <>
      <Slate ... >
       .....
        <div className={"sidebar-wrapper"}>
          <CommentsSidebar />
            </div>
      </Slate>
    </>
);

We now move on to implementing a popular Comments Sidebar interaction found in editors:

Clicking on a comment thread in the sidebar should select/activate that comment thread. We also add a differential design treatment to highlight a comment thread in the sidebar if it’s active in the editor. To be able to do so, we use the Recoil atom — activeCommentThreadIDAtom. Let’s update the CommentThread component to support this.

# src/components/CommentsSidebar.js

function CommentThread({ id }) {

const [activeCommentThreadID, setActiveCommentThreadID] = useRecoilState(
    activeCommentThreadIDAtom
  );

const onClick = useCallback(() => {
setActiveCommentThreadID(id); }, [id, setActiveCommentThreadID]); ... return ( <Card body={true} className={classNames({ "comment-thread-container": true, "is-active": activeCommentThreadID === id,
})} onClick={onClick} > .... </Card> );

If we look closely, we have a bug in our implementation of sync-ing the active comment thread with the sidebar. As we click on different comment threads in the sidebar, the correct comment thread is indeed highlighted in the editor. However, the Comment Popover doesn’t actually move to the changed active comment thread. It stays where it was first rendered. If we look at the implementation of the Comment Popover, it renders itself against the first text node in the editor’s selection. At that point in the implementation, the only way to select a comment thread was to click on a text node so we could conveniently rely on the editor's selection since it was updated by Slate as a result of the click event. In the above onClick event, we don’t update the selection but merely update the Recoil atom value causing Slate’s selection to remain unchanged and hence the Comment Popover doesn’t move.

A solution to this problem is to update the editor’s selection along with updating the Recoil atom when the user clicks on the comment thread in the sidebar. The steps do this are:

  1. Find all text nodes that have this comment thread on them that we are going to set as the new active thread.
  2. Sort these text nodes in the order in which they appear in the document (We use Slate’s Path.compare API for this).
  3. Compute a selection range that spans from the start of the first text node to the end of the last text node.
  4. Set the selection range to be the editor’s new selection (using Slate’s Transforms.select API).

If we just wanted to fix the bug, we could just find the first text node in Step #1 that has the comment thread and set that to be the editor’s selection. However, it feels like a cleaner approach to select the entire comment range as we really are selecting the comment thread.

Let’s update the onClick callback implementation to include the steps above.

const onClick = useCallback(() => {

    const textNodesWithThread = Editor.nodes(editor, {
      at: [],
      mode: "lowest",
      match: (n) => Text.isText(n) && getCommentThreadsOnTextNode(n).has(id),
    });

    let textNodeEntry = textNodesWithThread.next().value;
    const allTextNodePaths = [];

    while (textNodeEntry != null) {
      allTextNodePaths.push(textNodeEntry[1]);
      textNodeEntry = textNodesWithThread.next().value;
    }

    // sort the text nodes
    allTextNodePaths.sort((p1, p2) => Path.compare(p1, p2));

    // set the selection on the editor
    Transforms.select(editor, {
      anchor: Editor.point(editor, allTextNodePaths[0], { edge: "start" }),
      focus: Editor.point(
        editor,
        allTextNodePaths[allTextNodePaths.length - 1],
        { edge: "end" }
      ),
    });

   // Update the Recoil atom value.
    setActiveCommentThreadID(id);
  }, [editor, id, setActiveCommentThreadID]);

Note: allTextNodePaths contains the path to all the text nodes. We use the Editor.point API to get the start and end points at that path. The first article goes through Slate’s Location concepts. They’re also well-documented on Slate’s documentation.

Let’s verify that this implementation does fix the bug and the Comment Popover moves to the active comment thread correctly. This time, we also test with a case of overlapping threads to make sure it doesn’t break there.

With the bug fix, we’ve enabled another sidebar interaction that we haven’t discussed yet. If we have a really long document and the user clicks on a comment thread in the sidebar that’s outside the viewport, we’d want to scroll to that part of the document so the user can focus on the comment thread in the editor. By setting the selection above using Slate’s API, we get that for free. Let’s see it in action below.

With that, we wrap our implementation of the sidebar. Towards the end of the article, we list out some nice feature additions and enhancements we can do to the Comments Sidebar that help elevate the Commenting and Review experience on the editor.

Resolving And Re-Opening Comments

In this section, we focus on enabling users to mark comment threads as ‘Resolved’ or be able to re-open them for discussion if needed. From an implementation detail perspective, this is the status metadata on a comment thread that we change as the user performs this action. From a user’s perspective, this is a very useful feature as it gives them a way to affirm that the discussion about something on the document has concluded or needs to be re-opened because there are some updates/new perspectives, and so on.

To enable toggling the status, we add a button to the CommentPopover that allows the user to toggle between the two statuses: open and resolved.

# src/components/CommentThreadPopover.js

export default function CommentThreadPopover({
  editorOffsets,
  selection,
  threadID,
}) {
  …
  const [threadData, setCommentThreadData] = useRecoilState(
    commentThreadsState(threadID)
  );

  ...

  const onToggleStatus = useCallback(() => {
    const currentStatus = threadData.status;
    setCommentThreadData((threadData) => ({
      ...threadData,
      status: currentStatus === "open" ? "resolved" : "open",
    }));
  }, [setCommentThreadData, threadData.status]);

  return (
    <NodePopover
      ...
      header={
        <Header
          status={threadData.status}
          shouldAllowStatusChange={threadData.comments.length > 0}
          onToggleStatus={onToggleStatus}
        />
      }
    >
      <div className={"comment-list"}>
          ...
      </div>
    </NodePopover>
  );
}

function Header({ onToggleStatus, shouldAllowStatusChange, status }) {
  return (
    <div className={"comment-thread-popover-header"}>
      {shouldAllowStatusChange && status != null ? (
        <Button size="sm" variant="primary" onClick={onToggleStatus}>
          {status === "open" ? "Resolve" : "Re-Open"}
        </Button>
      ) : null}
    </div>
  );
}

Before we test this, let’s also give the Comments Sidebar a differential design treatment for resolved comments so that the user can easily detect which comment threads are un-resolved or open and focus on those if they want to.

# src/components/CommentsSidebar.js

function CommentThread({ id }) {
  ...
  const { comments, status } = useRecoilValue(commentThreadsState(id));

 ...
  return (
    <Card
      body={true}
      className={classNames({
        "comment-thread-container": true,
        "is-resolved": status === "resolved",
        "is-active": activeCommentThreadID === id,
      })}
      onClick={onClick}
    >
       ...
</Card> ); }

Conclusion

In this article, we built the core UI infrastructure for a Commenting System on a Rich Text Editor. The set of functionalities we add here act as a foundation to build a richer Collaboration Experience on an editor where collaborators could annotate parts of the document and have conversations about them. Adding a Comments Sidebar gives us a space to have more conversational or review-based functionalities to be enabled on the product.

Along those lines, here are some of features that a Rich Text Editor could consider adding on top of what we built in this article:

  • Support for @ mentions so collaborators could tag one another in comments;
  • Support for media types like images and videos to be added to comment threads;
  • Suggestion Mode at the document level that allows reviewers to make edits to the document that appear as suggestions for changes. One could refer to this feature in Google Docs or Change Tracking in Microsoft Word as examples;
  • Enhancements to the sidebar to search conversations by keyword, filter threads by status or comment author(s), and so on.

Hi everyone, I’m blackroyal321

The Bespoke Group of Entrepreneurs
The Bespoke Group of Entrepreneurs (Bespoke-G-Ent) is a community of like-minded people that interact over startups, networking and trends. Our products are there to serve the needs that an every day entrepreneur may incur. Be it networking, style, productivity or health. We also have an eco-friendly selection.