Guides

How to Display Form Entries on the Front End of WordPress

Illustration for displaying form entries on the front end

Sooner or later, someone asks for it. The form has been collecting submissions for months, and now a client, a colleague or a member of your team wants those submissions on the site — a public directory, a wall of testimonials, a table the office manager can read without you granting them WordPress admin access. It sounds like a five-minute job. It usually isn’t, and not for technical reasons. The hard part is deciding who is allowed to see what, and the tooling makes it far too easy to skip that decision entirely.

This post covers the legitimate reasons to display form entries on the front end, four ways to do it ranked by effort, and — at length, because this is where sites get hurt — the risks the tutorial you were about to follow won’t mention.

When displaying entries is actually the right call

Let’s start with the cases where this is a genuinely good idea, because they exist and they’re common.

  • A public directory. A trade association takes member applications through a form and wants an approved, searchable listing of members. The data was collected for publication, the members know it, and the form said so.
  • Member-submitted listings. Classifieds, event calendars, job boards, “add your business” pages. The form is a lightweight substitute for a proper post-submission workflow.
  • A testimonials wall. People submit a quote and a name, you approve the good ones, they appear on a page. Low risk if you display only the fields you asked permission for.
  • An internal dashboard for non-admin staff. This is the most frequent real request, and it’s almost never a front-end problem at all. Someone in sales needs to read enquiries. Rather than handing them the keys to the whole install, the instinct is to build a page that lists entries. That instinct is right about the goal and wrong about the location.

Notice how different that last one is. Three of these want public data. One wants restricted data and has been misfiled as a display problem. Sorting your own request into the right bucket before you build anything will save you a rebuild later.

The four routes, ranked by effort

1. GravityView, if you’re on Gravity Forms

We’ll be straight about this: GravityView is the best-in-class answer, and it comes with two hard constraints. It is Gravity-Forms-only, and it is paid — as is Gravity Forms itself, so you’re looking at two commercial licences before you render a single row. If you already run Gravity Forms on a site with a budget, it’s an easy recommendation: sortable tables, single-entry pages, front-end editing, approval workflows and search filters without writing code, and access controls that were designed in rather than bolted on.

If you’re not on Gravity Forms, the calculus changes completely. Switching form plugins purely to unlock a views add-on means migrating every existing entry, and entry migration between builders is genuinely painful.

2. Your builder’s own shortcode or views add-on

Several builders ship something. Formidable Forms has views built into its Pro tier and they’re capable. WPForms has an entries shortcode of limited flexibility. Ninja Forms and Fluent Forms have varying third-party or first-party options. Contact Form 7 has essentially nothing native — which is precisely why “gravityview vs contact form 7 views” is a query people type; there is no equivalent, and CF7 users typically bolt on a database add-on first just to store entries, then a second plugin to display them.

Check what your builder offers before assuming you need to build something. The native route usually respects the plugin’s own permission model, which is worth a lot.

3. A custom WP_Query loop over a custom post type

If your entries are stored as a custom post type — which several storage add-ons do — you can write a normal WordPress loop and have complete control over markup, styling and, crucially, which fields appear. It’s more work up front and yours to maintain forever, but it’s the only route where nothing can leak by accident, because nothing appears unless you print it.

An illustrative sketch:

$entries = new WP_Query( array( 'post_type' => 'form_entry', 'post_status' => 'publish', 'posts_per_page' => 20 ) ); if ( current_user_can( 'edit_others_posts' ) && $entries->have_posts() ) { while ( $entries->have_posts() ) { $entries->the_post(); $name = get_post_meta( get_the_ID(), 'entry_name', true ); echo '<tr><td>' . esc_html( $name ) . '</td></tr>'; } } wp_reset_postdata();

Read that snippet as a shape, not as production code. Two things in it are doing the heavy lifting and both are easy to drop. The current_user_can() check is what stops the table rendering for the public — without it, the loop happily prints everyone’s data to anyone with the URL. And esc_html() on every single output is what stops a submitted value containing markup from executing in your visitors’ browsers; form entries are untrusted input by definition, and a directory page is a stored-XSS vector waiting for a careless echo. If you take one habit from this post, make it: allow-list the fields you print, escape every one of them, and gate the whole block on a capability.

4. A table plugin fed by a CSV export

The unglamorous option that is often correct. Export the entries you want, hand-check the file, remove the columns nobody needs, and feed it to a table plugin. It’s manual, it goes stale, and it is by far the safest approach because a human looks at the data before it becomes public. For a directory that changes monthly, this beats a live query. We’ve written separately about storing and exporting WordPress form entries if that’s the step you’re missing.

Comparing the approaches

Approach Effort Cost Works with PII safety
GravityView Low Paid, plus paid Gravity Forms Gravity Forms only Good — real access controls, but easy to over-share by default
Native shortcode / views add-on Low to medium Free to mid-tier paid Formidable, WPForms, Fluent, Ninja (varies); not CF7 Varies wildly — read the docs on visibility
Custom WP_Query loop High Developer time Anything storing entries as posts Best, if you allow-list and escape; worst if you don’t
Table plugin + CSV Medium, recurring Free to cheap Any builder that exports Excellent — a human reviews every publish

The risks, which are the actual story

Most tutorials on this topic end at “and now your entries are on the page!” Here is what happens next.

You publish more than you meant to. Views tools default to showing all fields. That includes the hidden ones — IP address, user agent, referrer URL, internal admin notes, the UTM parameters your marketing team added. We have seen a “simple” directory page ship with submitter IP addresses in a column because nobody unchecked it.

Email addresses get harvested. A public page listing a hundred email addresses in plain text is a scraper’s idea of a good afternoon. If contact is the point, use a relay form rather than printing the address.

File uploads are usually not protected. This is the sharpest edge. Many form plugins store uploads in a predictable path under wp-content/uploads/ with no access control at all. Publishing a link to someone’s uploaded CV means publishing the CV — and often, because the directory is browsable or the filenames are sequential, publishing everyone else’s too. Test this by opening an upload URL in a logged-out private window before you launch. If it loads, you have a problem that no amount of front-end permission logic will fix.

You may have changed your lawful basis. Data collected for “we’ll get back to you about your enquiry” cannot be republished as a public directory without consent. This is a straightforward GDPR issue and it’s worth reading our notes on GDPR and WordPress forms before you make anything public. Add a retention plan while you’re there — indefinitely published personal data is hard to defend.

The safe default is an allow-list, not a deny-list. Start from zero fields visible and add the ones you can justify. Every tool that starts from “all fields” and asks you to hide things will eventually leak something, because a new field added to the form six months from now inherits the visible default.

If what you actually wanted was internal access

Come back to that fourth use case. If the request was “let the office read the enquiries”, a public page is the wrong shape entirely — you’d be building a permission system on the front end to solve a problem the admin area already solves. The better answer is a role with limited capabilities and a decent entry-management screen behind the login. Our roundup of entry management plugins covers the options.

Full disclosure: our own free plugin, EntryVault, was built for exactly this shape of problem — it gives non-admin staff a Kanban board of submissions to triage inside wp-admin, so entries stay behind the login instead of being pushed onto a public page to make them shareable.

A short checklist before you publish

  1. List every field on the form, including hidden ones, and mark each as public or not.
  2. Open the page logged out. Then open it in a private window. Then check the REST API and any AJAX endpoint the view uses.
  3. Open one file-upload URL logged out. Fix it if it loads.
  4. Confirm the form’s privacy notice matches what you’re now publishing.
  5. Decide how an entry gets removed, and who can do it, before the first removal request arrives.

None of this is exotic. It’s just the part that gets skipped because “show the submissions on a page” sounds like a display task rather than a data-governance one. Twenty minutes on the field list avoids the awkward email later.

Frequently asked questions

Is there a GravityView equivalent for Contact Form 7?

Not really. Contact Form 7 doesn’t store entries natively, so you need a database add-on to save them first, and then a separate table or listing plugin to display them. The combination works but it’s two plugins doing what GravityView does as one, with far less control over permissions. For CF7 sites we usually recommend the CSV-plus-table-plugin route, or moving the requirement into the admin area instead.

Can I show form entries only to logged-in users?

Yes, and you should whenever the data isn’t genuinely meant to be public. In a custom loop that’s a current_user_can() or is_user_logged_in() check wrapping the output. In a views plugin, look for its visibility or role settings and test them logged out — some tools restrict the page but still expose the underlying data through a REST or AJAX endpoint.

Usually not. Most form plugins write uploads into the standard uploads directory, which is publicly served, so the file is reachable by anyone who guesses or discovers the URL. Not linking to it is obscurity, not protection. If uploads contain anything personal, store them outside the web root or serve them through a PHP handler that checks capabilities before streaming the file.