Overloaded equals operator
There is in fact a difference in semantics between the two comparisons when you are comparing null with a type that has overloaded the == operator. foo is null will use direct reference comparison to determine the result, whereas foo == null will of course run the overloaded == operator if it exists.
In this example I have introduced a "bug" in the overloaded == operator, causing it to always throw an exception if the second argument is null:
void Main()
{
    Foo foo = null;
    if (foo is null) Console.WriteLine("foo is null"); // This condition is met
    if (foo == null) Console.WriteLine("foo == null"); // This will throw an exception
}
public class Foo
{
    public static bool operator ==(Foo foo1, Foo foo2)
    {
        if (object.Equals(foo2, null)) throw new Exception("oops");
        return object.Equals(foo1, foo2);
    }
    // ...
}
The IL code for foo is null uses the ceq instruction to perform a direct reference comparison:
IL_0003:  ldloc.0     // foo
IL_0004:  ldnull      
IL_0005:  ceq
The IL code for foo == null uses a call to the overloaded operator:
IL_0016:  ldloc.0     // foo
IL_0017:  ldnull      
IL_0018:  call        UserQuery+Foo.op_Equality
So the difference is, that if you use == you risk running user code (which can potentially have unexpected behavior or performance problems).
Restriction on generics
Using the is null construct restricts the type to a reference type. The compiler ensures this, which means you cannot use is null on a value type. If you have a generic method, you will not be able to use is null unless the generic type is constrained to be a reference type.
bool IsNull<T>(T item) => item is null;                  // Compile error: CS0403
bool IsNull<T>(T item) => item == null;                  // Works
bool IsNull<T>(T item) where T : class => item is null;  // Works
Thanks to David Augusto Villa for pointing this out.