diff --git a/nicknames/isValid.go b/nicknames/isValid.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2edde92bde01310a68eba0e7ce7868ef858acc9
--- /dev/null
+++ b/nicknames/isValid.go
@@ -0,0 +1,43 @@
+package nicknames
+
+import (
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+)
+
+const (
+	MinNicknameLength = 3
+	MaxNicknameLength = 24
+)
+
+var ErrNicknameTooShort = errors.Errorf("nicknames must be at least "+
+	"%d characters in length", MinNicknameLength)
+var ErrNicknameTooLong = errors.Errorf("nicknames must be %d "+
+	"characters in length or less", MaxNicknameLength)
+
+// IsValid checks if a nickname is valid.
+//
+// Rules:
+//   - A nickname must not be longer than 24 characters.
+//   - A nickname must not be shorter than 1 character.
+//   - A nickname may be blank, this will be treated by the system as
+//     no nickname
+//
+// TODO: Add character filtering.
+func IsValid(nick string) error {
+	if nick == "" {
+		jww.INFO.Printf("empty nickname passed, treating like no " +
+			"nickname")
+		return nil
+	}
+	runeNick := []rune(nick)
+	if len(runeNick) < MinNicknameLength {
+		return errors.WithStack(ErrNicknameTooShort)
+	}
+
+	if len(runeNick) > MaxNicknameLength {
+		return errors.WithStack(ErrNicknameTooLong)
+	}
+
+	return nil
+}
diff --git a/nicknames/isValid_test.go b/nicknames/isValid_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bc2ce5a11513f687da89873b2b351a65eff40121
--- /dev/null
+++ b/nicknames/isValid_test.go
@@ -0,0 +1,56 @@
+package nicknames
+
+import (
+	"github.com/pkg/errors"
+	"testing"
+)
+
+func TestIsNicknameValid(t *testing.T) {
+
+	// test that behavior for an empty nickname is correct
+
+	if err := IsValid(""); err != nil {
+		t.Errorf("Empty nickname should be valid, received: %+v", err)
+	}
+
+	nicknameSource := "Sodium, atomic number 11, was first isolated by Humphry " +
+		"Davy in 1807. A chemical component of salt, he named it Na in honor " +
+		"of the saltiest region on earth, North America."
+
+	// test that behavior for too short nicknames is correct
+	for i := 1; i < MinNicknameLength; i++ {
+		nick := nicknameSource[:i]
+
+		if err := IsValid(nick); err != nil &&
+			!errors.Is(err, ErrNicknameTooShort) {
+			t.Errorf("Wrong error returned from nicknames.IsValid() "+
+				"with too short input of length %d: %+v", i, err)
+		} else if err == nil {
+			t.Errorf("No error returned from nicknames.IsValid() "+
+				"with too short input of length %d", i)
+		}
+	}
+
+	// test that behavior for too long nicknames is correct
+	for i := MaxNicknameLength + 1; i < MaxNicknameLength*5; i++ {
+		nick := nicknameSource[:i]
+
+		if err := IsValid(nick); err != nil &&
+			!errors.Is(err, ErrNicknameTooLong) {
+			t.Errorf("Wrong error returned from nicknames.IsValid() "+
+				"with too long input of length %d: %+v", i, err)
+		} else if err == nil {
+			t.Errorf("No error returned from nicknames.IsValid() "+
+				"with too long input of length %d", i)
+		}
+	}
+
+	// test that behavior for valid nicknames is correct
+	for i := MinNicknameLength; i <= MaxNicknameLength; i++ {
+		nick := nicknameSource[:i]
+		if err := IsValid(nick); err != nil {
+			t.Errorf("Error returned from nicknames.IsValid() "+
+				"with valid nickname of input of length %d: %+v", i, err)
+		}
+	}
+}