Forms & Strong Parameters

Reviewed & published by Brayan K

Forms are how users hand data to your app. Rails gives you form_with to build them, form helpers to render each field, and strong parameters to accept only the fields you intend to — safely.

Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll build a model-backed form, whitelist input with params.require.permit, understand CSRF protection, and show a flash message.

What You'll Learn in This Lesson

1️⃣ Building a Form with form_with

In the view, form_with model: @post creates a form bound to a model. Helpers like form.text_field and form.submit render each control, pre-fill values when editing, and the CSRF token is added automatically.

<%# app/views/posts/new.html.erb %>
<%# form_with builds a form bound to the @post model. It points at the
    posts controller, adds the CSRF token automatically, and on submit
    sends params nested under :post (e.g. params[:post][:title]). %>

<%= form_with model: @post do |form| %>
  <% if @post.errors.any? %>
    <div class="errors">
      <%= @post.errors.full_messages.to_sentence %>
    </div>
  <% end %>

  <div>
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <%= form.submit "Save Post" %>
<% end %>

<%# Helpers like text_field, text_area, label, and submit generate the
    matching HTML and pre-fill values from @post when editing. %>

2️⃣ Strong Parameters in the Controller

In the controller, never pass raw params to a model. Use params.require(:post).permit(:title, :body) to whitelist fields, then branch on save: redirect with a flash on success, or re-render the form on failure.

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def new
    @post = Post.new            # an empty object for the form
  end

  def create
    @post = Post.new(post_params)   # use the PERMITTED params only
    if @post.save
      # flash[:notice] survives one redirect to show a success message
      redirect_to @post, notice: "Post created!"
    else
      # re-render the form so validation errors display
      render :new, status: :unprocessable_entity
    end
  end

  private

  # Strong parameters: require the :post key, then whitelist fields.
  # Passing raw params to Post.new would raise ForbiddenAttributesError.
  def post_params
    params.require(:post).permit(:title, :body)
  end
end

Your turn. Finish a CommentsController#create with a proper strong-parameters method and a flash message.

# 🎯 YOUR TURN — finish a CommentsController create action.
# The form submits params[:comment][:body] (and a post_id).

class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.new(comment_params)  # 👉 use strong params
    if @comment.save
      redirect_to @post, notice: "Comment added!" # 👉 flash message
    else
      redirect_to @post, alert: "Comment was invalid."
    end
  end

  private

  # 1) Require the :comment key and permit ONLY :body
  def comment_params
    params.require(:comment).permit(:body)         # 👉 TODO: complete this
  end
end

# ✅ A POST with comment[body]="Nice!" should create a comment and
#    redirect back to the post with a success flash.

Mini-Challenge: A Contact Form

Build a form_with for a @message and a controller that whitelists name, email, and body, then shows a flash on success.

# 🎯 MINI-CHALLENGE: a contact form and controller.
#
# 1. Build a form_with for a @message (fields: name, email, body).
# 2. In MessagesController#create, define message_params with
#    params.require(:message).permit(:name, :email, :body).
# 3. On success: redirect_to root_path, notice: "Thanks!"
#    On failure: render :new, status: :unprocessable_entity.
#
# Note WHERE CSRF protection comes from: form_with adds the hidden
# authenticity_token for you, and the controller verifies it.
#
# ✅ Goal: a working create action that only accepts whitelisted fields
#          and shows a flash message on success.

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Forms & Strong Parameters

GoalCode
Model-backed formform_with model: @post
Text inputform.text_field :title
Submit buttonform.submit "Save"
Whitelist paramsparams.require(:post).permit(:title)
Success messageredirect_to @post, notice: "..."
CSRF tokenadded by form_with automatically

🎉 Lesson Complete!

Practice quiz

Which helper is the modern way to build a form bound to a model?

  • form_tag
  • form_for
  • form_with
  • make_form

Answer: form_with. form_with is the current unified form helper in Rails.

What is the purpose of strong parameters?

  • To speed up the database
  • To whitelist which params may be mass-assigned
  • To validate the model
  • To encrypt the form

Answer: To whitelist which params may be mass-assigned. Strong parameters let only permitted attributes through to the model.

Which call correctly permits title and body for a post?

  • params.permit(:post)
  • params.allow(:title, :body)
  • params.require(:post).permit(:title, :body)
  • params[:post].permit_all

Answer: params.require(:post).permit(:title, :body). params.require(:post).permit(:title, :body) requires the post key and whitelists fields.

What does Rails' CSRF protection guard against?

  • SQL injection
  • Cross-site request forgery on state-changing requests
  • Cross-site scripting only
  • Slow database queries

Answer: Cross-site request forgery on state-changing requests. CSRF protection blocks forged state-changing requests from other sites.

How does form_with normally include the CSRF token?

  • You must add it by hand
  • It is added automatically as a hidden field
  • Only on GET forms
  • It is sent as a cookie

Answer: It is added automatically as a hidden field. form_with injects the authenticity_token hidden field automatically.

Inside form_with |form|, which helper renders a text field for title?

  • form.input :title
  • text_field(:title)
  • form.field :title, :text
  • form.text_field :title

Answer: form.text_field :title. form.text_field :title renders the input bound to the model attribute.

What is the flash used for?

  • Permanent settings
  • A short-lived message shown on the next request
  • Caching query results
  • Storing the CSRF token

Answer: A short-lived message shown on the next request. The flash holds a message available to the immediately following request, ideal after a redirect.

Why call permit on params instead of using them directly?

  • For faster JSON parsing
  • To avoid ForbiddenAttributesError and unwanted mass assignment
  • To translate the params
  • It is purely stylistic

Answer: To avoid ForbiddenAttributesError and unwanted mass assignment. Passing raw params to a model raises ForbiddenAttributesError; permit whitelists safe fields.

Which flash key is conventional for a success message after creating a record?

  • flash[:notice]
  • flash[:error]
  • flash[:warning]
  • flash[:debug]

Answer: flash[:notice]. flash[:notice] (often via notice:) is the conventional success message.

If a create action fails validation, what is the usual response?

  • Redirect to the index
  • Re-render the form with the invalid object and status :unprocessable_entity
  • Raise a 500 error
  • Silently ignore it

Answer: Re-render the form with the invalid object and status :unprocessable_entity. You re-render :new with the unsaved record so its errors display, using 422.

Continue this course

Frequently asked questions

Why does Rails require strong parameters?

Strong parameters protect against mass-assignment attacks. Without them, a malicious user could submit extra fields — like admin: true — and have them written straight to your model. By calling params.require(:post).permit(:title, :body) you explicitly whitelist which attributes may be assigned, and any others are silently dropped. If you try to pass raw, unpermitted params into Post.new, Rails raises ForbiddenAttributesError. Keeping the permit list in a private method (often post_params) is the standard pattern and makes the allowed fields easy to audit.

What is CSRF protection and how does form_with handle it?

CSRF (cross-site request forgery) is an attack where another site tricks a logged-in user's browser into submitting a state-changing request to your app. Rails defends against it by embedding a per-session authenticity token in every non-GET form and verifying it on the server; requests without a valid token are rejected. The good news is form_with adds that hidden token field automatically, so you rarely think about it. The protection is switched on by default via protect_from_forgery behaviour in ApplicationController. For JavaScript requests, the token is also exposed in a meta tag.

What is the difference between form_with, form_for, and form_tag?

form_with is the modern, unified helper introduced in Rails 5.1 and is what you should use today. It replaces the two older helpers: form_for, which was bound to a model object, and form_tag, which built a plain form not tied to a model. form_with covers both cases — pass model: @post for a model-backed form, or url: some_path for a standalone form. The older helpers still work for backward compatibility, but new code and the official guides use form_with throughout.

How does the flash work after a redirect?

The flash is a special, short-lived hash for passing a message to the very next request. You typically set it during a redirect — redirect_to @post, notice: "Saved!" — and the layout displays flash[:notice] when the redirected page renders. It survives exactly one redirect and is then cleared, which is why it is perfect for one-off confirmations or errors. Common keys are :notice for success and :alert for problems, though you can use any key. If you need a message on a render (not a redirect) within the same request, use flash.now instead.